added error handling for ngx-data-apollo
added handle wrapper
This commit is contained in:
		
							parent
							
								
									e5133b2f95
								
							
						
					
					
						commit
						ab99d5f076
					
				| @ -8,8 +8,10 @@ | ||||
|     "test": "ng test", | ||||
|     "lint": "ng lint", | ||||
|     "e2e": "ng e2e", | ||||
|     "build-lib": "ng build @poweredsoft/ngx-data", | ||||
|     "publish-lib": "npm publish dist/poweredsoft/ngx-data" | ||||
|     "build-data": "ng build @poweredsoft/ngx-data", | ||||
|     "build-apollo": "ng build @poweredsoft/ngx-data-apollo", | ||||
|     "publish-data": "npm publish dist/poweredsoft/ngx-data", | ||||
|     "publish-apollo": "npm publish dist/poweredsoft/ngx-data-apollo" | ||||
|   }, | ||||
|   "private": true, | ||||
|   "dependencies": { | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@poweredsoft/ngx-data-apollo", | ||||
|   "version": "0.0.2", | ||||
|   "version": "0.0.3", | ||||
|   "peerDependencies": { | ||||
|     "@poweredsoft/data": "^0.0.26", | ||||
|     "@angular/common": "^8.2.4", | ||||
|  | ||||
| @ -1,24 +1,32 @@ | ||||
| import { IDataSourceOptions, IQueryCriteria, IDataSourceTransportOptions, IDataSourceQueryAdapterOptions, IDataSourceCommandAdapterOptions, IAdvanceQueryAdapter, IFilter, IAggregate, ISort, IGroup, ISimpleFilter, ICompositeFilter, IQueryExecutionGroupResult, IQueryExecutionResult, IAggregateResult, IGroupQueryResult } from '@poweredsoft/data'; | ||||
| import { IDataSourceOptions, IQueryCriteria, IDataSourceTransportOptions, IDataSourceQueryAdapterOptions, IDataSourceCommandAdapterOptions, IAdvanceQueryAdapter, IFilter, IAggregate, ISort, IGroup, ISimpleFilter, ICompositeFilter, IQueryExecutionGroupResult, IQueryExecutionResult, IAggregateResult, IGroupQueryResult, IResolveCommandModelEvent, IDataSourceErrorMessage, IDataSourceValidationError } from '@poweredsoft/data'; | ||||
| import { Apollo } from 'apollo-angular'; | ||||
| import { IGraphQLAdvanceQueryResult, IGraphQLAdvanceQueryInput, IGraphQLAdvanceQueryFilterInput, IGraphQLAdvanceQueryAggregateInput, IGraphQLAdvanceQuerySortInput, IGraphQLAdvanceQueryGroupInput, FilterType, IGraphQLAdvanceQueryAggregateResult, IGraphQLVariantResult, AggregateType, IGraphQLAdvanceGroupResult } from './models'; | ||||
| import gql from 'graphql-tag'; | ||||
| import { DocumentNode } from 'graphql'; | ||||
| import { map, catchError } from 'rxjs/operators'; | ||||
| import { Observable } from 'rxjs'; | ||||
| import { DocumentNode, GraphQLError } from 'graphql'; | ||||
| import { map, catchError, switchMap } from 'rxjs/operators'; | ||||
| import { Observable, throwError } from 'rxjs'; | ||||
| import { FetchResult } from 'apollo-link'; | ||||
| import { of } from 'zen-observable'; | ||||
| import { ApolloError } from 'apollo-client'; | ||||
| 
 | ||||
| export class GraphQLDataSourceOptionsBuilder<TModel, TKey> { | ||||
| 
 | ||||
|     private _commands: { [key: string] : IDataSourceCommandAdapterOptions<any> }; | ||||
|     querySelect: string; | ||||
|     private _commands: { [key: string] : IDataSourceCommandAdapterOptions<any> } = {}; | ||||
| 
 | ||||
|     constructor(private apollo: Apollo,  | ||||
|         private queryName: string,  | ||||
|         private queryInputName: string,  | ||||
|         private querySelect: string,  | ||||
|         querySelect: string | string[],  | ||||
|         private keyResolver: (model: TModel) => TKey,  | ||||
|         private defaultCriteria: IQueryCriteria,  | ||||
|         private manageNotificationMessage: boolean)  | ||||
|     { | ||||
|         if (Array.isArray(querySelect)) | ||||
|             this.querySelect = querySelect.join(' '); | ||||
|         else | ||||
|             this.querySelect = querySelect; | ||||
|     } | ||||
|      | ||||
|     create(): IDataSourceOptions<TModel> { | ||||
|         let ret: IDataSourceOptions<TModel> = { | ||||
|             resolveIdField: this.keyResolver, | ||||
| @ -28,6 +36,7 @@ export class GraphQLDataSourceOptionsBuilder<TModel, TKey> { | ||||
|         }; | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     protected createTransport(): IDataSourceTransportOptions<TModel> { | ||||
|         let ret: IDataSourceTransportOptions<TModel> = { | ||||
|             query: this.createQuery(), | ||||
| @ -122,6 +131,7 @@ export class GraphQLDataSourceOptionsBuilder<TModel, TKey> { | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
|      | ||||
|     private createGraphQLQuery(query: IQueryCriteria): DocumentNode { | ||||
|         return gql` | ||||
|             query getAll($criteria: ${this.queryInputName}) { | ||||
| @ -163,14 +173,63 @@ export class GraphQLDataSourceOptionsBuilder<TModel, TKey> { | ||||
|             groups: query.groups ? query.groups.map(this.convertGroup.bind(this)) : null | ||||
|         }; | ||||
|         return ret; | ||||
|      | ||||
|     } | ||||
| 
 | ||||
|     public addMutation<TMutation, TMutationResult>(name: string, handle: (command: TMutation) => Observable<TMutationResult>) { | ||||
| 
 | ||||
|         this._commands[name] = <IDataSourceCommandAdapterOptions<TMutation>> { | ||||
|          | ||||
|     /*public addMutationTest<TMutation, TMutationResult>(name: string, mutationName: string, mutationSelect?: string, resolveCommandModel?: (event: IResolveCommandModelEvent<TModel>) => Observable<TMutation & any>) { | ||||
|         this._commands[name] = <IDataSourceCommandAdapterOptions<TModel>> { | ||||
|             adapter: { | ||||
|                 handle: handle | ||||
|             } | ||||
|                 handle: this.apollo.use() | ||||
|             }, | ||||
|             resolveCommandModel: resolveCommandModel | ||||
|         }; | ||||
| 
 | ||||
|         return this; | ||||
|     }*/ | ||||
| 
 | ||||
|     public addMutation<TMutation, TMutationResult>(name: string, mutationName: string, handle: (command: TMutation) => Observable<FetchResult<TMutationResult>>, resolveCommandModel?: (event: IResolveCommandModelEvent<TModel>) => Observable<TMutation & any>) { | ||||
|         const handleWrapper = command => { | ||||
|             return handle(command) | ||||
|                 .pipe( | ||||
|                     map(result => { | ||||
|                         return result.data[mutationName]; | ||||
|                     }), | ||||
|                     catchError((error: any) => { | ||||
|                         // should handle bad request with exception
 | ||||
|                         // should handle bad request with validation
 | ||||
|                         // should handle forbidden result 403
 | ||||
|                         // should handle not authorized result 401
 | ||||
| 
 | ||||
|                         const apolloError : ApolloError = error; | ||||
|                         if (!apolloError.networkError) { | ||||
|                             const validationError = apolloError.graphQLErrors.find(t => t.extensions.code == 'ValidationError'); | ||||
|                             if (validationError) { | ||||
|                                 const extensions = validationError.extensions; | ||||
|                                 const result = Object.keys(extensions).filter(t => t != 'code').reduce((prev, attributeName) => { | ||||
|                                     prev[attributeName] = extensions[attributeName]; | ||||
|                                     return prev; | ||||
|                                 }, {}); | ||||
| 
 | ||||
|                                 return throwError(<IDataSourceValidationError>{ | ||||
|                                     type: 'validation', | ||||
|                                     errors: result | ||||
|                                 }); | ||||
|                             } | ||||
|                         } | ||||
| 
 | ||||
|                         return throwError(<IDataSourceErrorMessage>{ | ||||
|                             type: 'message', | ||||
|                             message: apolloError.message | ||||
|                         }); | ||||
|                     }) | ||||
|                 ); | ||||
|         }; | ||||
|          | ||||
|         this._commands[name] = <IDataSourceCommandAdapterOptions<TModel>> { | ||||
|             adapter: { | ||||
|                 handle: handleWrapper | ||||
|             }, | ||||
|             resolveCommandModel: resolveCommandModel | ||||
|         }; | ||||
| 
 | ||||
|         return this; | ||||
|  | ||||
| @ -8,4 +8,8 @@ | ||||
| 
 | ||||
| <button (click)="testGraphQL()"> | ||||
|   Test GraphQL query | ||||
| </button> | ||||
| 
 | ||||
| <button (click)="testGraphQLMutation()"> | ||||
|   Test GraphQL mutation | ||||
| </button> | ||||
| @ -1,8 +1,12 @@ | ||||
| import { Component, OnInit } from '@angular/core'; | ||||
| import { GenericRestDataSourceService } from 'projects/poweredsoft/ngx-data/src/public-api'; | ||||
| import { of } from 'rxjs'; | ||||
| import { DataSource } from '@poweredsoft/data'; | ||||
| import { of, Observable } from 'rxjs'; | ||||
| import { DataSource, IResolveCommandModelEvent } from '@poweredsoft/data'; | ||||
| import { GraphQLDataSourceService } from 'projects/poweredsoft/ngx-data-apollo/src/public-api'; | ||||
| import { Apollo } from 'apollo-angular'; | ||||
| import gql from 'graphql-tag'; | ||||
| import { map } from 'rxjs/operators'; | ||||
| import { DocumentNode } from 'graphql'; | ||||
| 
 | ||||
| 
 | ||||
| export interface IContact { | ||||
| @ -17,6 +21,11 @@ export interface ICustomerModel { | ||||
|   lastName: string; | ||||
| } | ||||
| 
 | ||||
| export interface IFooCommand { | ||||
|   amount: number; | ||||
|   comment: string; | ||||
| } | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-root', | ||||
|   templateUrl: './app.component.html', | ||||
| @ -26,7 +35,7 @@ export class AppComponent implements OnInit { | ||||
|   title = 'ngx-data'; | ||||
|   dataSource: DataSource<ICustomerModel>; | ||||
| 
 | ||||
|   constructor(genericService: GenericRestDataSourceService, private graphQLService: GraphQLDataSourceService) { | ||||
|   constructor(genericService: GenericRestDataSourceService, private apollo: Apollo, private graphQLService: GraphQLDataSourceService) { | ||||
|     const keyResolver = (model: ICustomerModel) => model.id; | ||||
| 
 | ||||
|     const transportOptions = genericService.createStandardRestTransportOptions('api/customer', keyResolver); | ||||
| @ -77,6 +86,70 @@ export class AppComponent implements OnInit { | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   testGraphQLMutation() { | ||||
|     const builder = this.graphQLService.createDataSourceOptionsBuilder<IContact, number>( | ||||
|       'contacts', | ||||
|       'GraphQLAdvanceQueryOfContactModelInput', | ||||
|       'id firstName lastName', | ||||
|       (m) => m.id, | ||||
|       { | ||||
|         groups: [ | ||||
|           { | ||||
|             path: 'sex' | ||||
|           } | ||||
|         ], | ||||
|         aggregates: [ | ||||
|           { | ||||
|             path: 'id', | ||||
|             type: 'Max' | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     ); | ||||
| 
 | ||||
|     builder.addMutation<IFooCommand, string>('create', 'foo', (command) => { | ||||
|       return this.apollo.mutate<string>({ | ||||
|           mutation: gql`mutation executeFoo($command: FooCommandInput) {
 | ||||
|             foo(params: $command) | ||||
|           }`,
 | ||||
|           variables: { | ||||
|             command: command | ||||
|           } | ||||
|         }); | ||||
|     }, | ||||
|     (event) => { | ||||
|       console.log(event); | ||||
|       if (event.model.id) | ||||
| 
 | ||||
|       return of({ | ||||
|         firstName: 'hello world' | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     const dataSourceOptions = builder.create(); | ||||
|     const dataSource = new DataSource<IContact>(dataSourceOptions); | ||||
|     let event: IResolveCommandModelEvent<IContact> = { | ||||
|       command: 'create', | ||||
|       model: { | ||||
|         id: 1, | ||||
|         firstName: 'hello', | ||||
|         lastName: 'world' | ||||
|       } | ||||
|     }; | ||||
| 
 | ||||
|     dataSource.resolveCommandModelByName(event) | ||||
|       .subscribe((result) => { | ||||
|         console.log('resolve result', result); | ||||
|       }); | ||||
| 
 | ||||
|     dataSource.executeCommandByName('create', { | ||||
|       amount: 0, | ||||
|       //comment: "hello"
 | ||||
|     }).subscribe((result) => { | ||||
|       console.log(result); | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   testGraphQL() { | ||||
|     const builder = this.graphQLService.createDataSourceOptionsBuilder<IContact, number>( | ||||
|       'contacts', | ||||
|  | ||||
| @ -3,7 +3,7 @@ import {ApolloModule, APOLLO_OPTIONS} from 'apollo-angular'; | ||||
| import {HttpLinkModule, HttpLink} from 'apollo-angular-link-http'; | ||||
| import {InMemoryCache} from 'apollo-cache-inmemory'; | ||||
| 
 | ||||
| const uri = 'https://localhost:5001/graphql'; // <-- add the URL of the GraphQL server here
 | ||||
| const uri = 'http://localhost:5000/graphql'; // <-- add the URL of the GraphQL server here
 | ||||
| export function createApollo(httpLink: HttpLink) { | ||||
|   return { | ||||
|     link: httpLink.create({uri}), | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user