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",
 | 
					    "test": "ng test",
 | 
				
			||||||
    "lint": "ng lint",
 | 
					    "lint": "ng lint",
 | 
				
			||||||
    "e2e": "ng e2e",
 | 
					    "e2e": "ng e2e",
 | 
				
			||||||
    "build-lib": "ng build @poweredsoft/ngx-data",
 | 
					    "build-data": "ng build @poweredsoft/ngx-data",
 | 
				
			||||||
    "publish-lib": "npm publish dist/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,
 | 
					  "private": true,
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "@poweredsoft/ngx-data-apollo",
 | 
					  "name": "@poweredsoft/ngx-data-apollo",
 | 
				
			||||||
  "version": "0.0.2",
 | 
					  "version": "0.0.3",
 | 
				
			||||||
  "peerDependencies": {
 | 
					  "peerDependencies": {
 | 
				
			||||||
    "@poweredsoft/data": "^0.0.26",
 | 
					    "@poweredsoft/data": "^0.0.26",
 | 
				
			||||||
    "@angular/common": "^8.2.4",
 | 
					    "@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 { Apollo } from 'apollo-angular';
 | 
				
			||||||
import { IGraphQLAdvanceQueryResult, IGraphQLAdvanceQueryInput, IGraphQLAdvanceQueryFilterInput, IGraphQLAdvanceQueryAggregateInput, IGraphQLAdvanceQuerySortInput, IGraphQLAdvanceQueryGroupInput, FilterType, IGraphQLAdvanceQueryAggregateResult, IGraphQLVariantResult, AggregateType, IGraphQLAdvanceGroupResult } from './models';
 | 
					import { IGraphQLAdvanceQueryResult, IGraphQLAdvanceQueryInput, IGraphQLAdvanceQueryFilterInput, IGraphQLAdvanceQueryAggregateInput, IGraphQLAdvanceQuerySortInput, IGraphQLAdvanceQueryGroupInput, FilterType, IGraphQLAdvanceQueryAggregateResult, IGraphQLVariantResult, AggregateType, IGraphQLAdvanceGroupResult } from './models';
 | 
				
			||||||
import gql from 'graphql-tag';
 | 
					import gql from 'graphql-tag';
 | 
				
			||||||
import { DocumentNode } from 'graphql';
 | 
					import { DocumentNode, GraphQLError } from 'graphql';
 | 
				
			||||||
import { map, catchError } from 'rxjs/operators';
 | 
					import { map, catchError, switchMap } from 'rxjs/operators';
 | 
				
			||||||
import { Observable } from 'rxjs';
 | 
					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> {
 | 
					export class GraphQLDataSourceOptionsBuilder<TModel, TKey> {
 | 
				
			||||||
 | 
					    querySelect: string;
 | 
				
			||||||
    private _commands: { [key: string] : IDataSourceCommandAdapterOptions<any> };
 | 
					    private _commands: { [key: string] : IDataSourceCommandAdapterOptions<any> } = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(private apollo: Apollo, 
 | 
					    constructor(private apollo: Apollo, 
 | 
				
			||||||
        private queryName: string, 
 | 
					        private queryName: string, 
 | 
				
			||||||
        private queryInputName: string, 
 | 
					        private queryInputName: string, 
 | 
				
			||||||
        private querySelect: string, 
 | 
					        querySelect: string | string[], 
 | 
				
			||||||
        private keyResolver: (model: TModel) => TKey, 
 | 
					        private keyResolver: (model: TModel) => TKey, 
 | 
				
			||||||
        private defaultCriteria: IQueryCriteria, 
 | 
					        private defaultCriteria: IQueryCriteria, 
 | 
				
			||||||
        private manageNotificationMessage: boolean) 
 | 
					        private manageNotificationMessage: boolean) 
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        if (Array.isArray(querySelect))
 | 
				
			||||||
 | 
					            this.querySelect = querySelect.join(' ');
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            this.querySelect = querySelect;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    create(): IDataSourceOptions<TModel> {
 | 
					    create(): IDataSourceOptions<TModel> {
 | 
				
			||||||
        let ret: IDataSourceOptions<TModel> = {
 | 
					        let ret: IDataSourceOptions<TModel> = {
 | 
				
			||||||
            resolveIdField: this.keyResolver,
 | 
					            resolveIdField: this.keyResolver,
 | 
				
			||||||
@ -28,6 +36,7 @@ export class GraphQLDataSourceOptionsBuilder<TModel, TKey> {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
        return ret;
 | 
					        return ret;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected createTransport(): IDataSourceTransportOptions<TModel> {
 | 
					    protected createTransport(): IDataSourceTransportOptions<TModel> {
 | 
				
			||||||
        let ret: IDataSourceTransportOptions<TModel> = {
 | 
					        let ret: IDataSourceTransportOptions<TModel> = {
 | 
				
			||||||
            query: this.createQuery(),
 | 
					            query: this.createQuery(),
 | 
				
			||||||
@ -122,6 +131,7 @@ export class GraphQLDataSourceOptionsBuilder<TModel, TKey> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    private createGraphQLQuery(query: IQueryCriteria): DocumentNode {
 | 
					    private createGraphQLQuery(query: IQueryCriteria): DocumentNode {
 | 
				
			||||||
        return gql`
 | 
					        return gql`
 | 
				
			||||||
            query getAll($criteria: ${this.queryInputName}) {
 | 
					            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
 | 
					            groups: query.groups ? query.groups.map(this.convertGroup.bind(this)) : null
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        return ret;
 | 
					        return ret;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    public addMutation<TMutation, TMutationResult>(name: string, handle: (command: TMutation) => Observable<TMutationResult>) {
 | 
					    /*public addMutationTest<TMutation, TMutationResult>(name: string, mutationName: string, mutationSelect?: string, resolveCommandModel?: (event: IResolveCommandModelEvent<TModel>) => Observable<TMutation & any>) {
 | 
				
			||||||
 | 
					        this._commands[name] = <IDataSourceCommandAdapterOptions<TModel>> {
 | 
				
			||||||
        this._commands[name] = <IDataSourceCommandAdapterOptions<TMutation>> {
 | 
					 | 
				
			||||||
            adapter: {
 | 
					            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;
 | 
					        return this;
 | 
				
			||||||
 | 
				
			|||||||
@ -9,3 +9,7 @@
 | 
				
			|||||||
<button (click)="testGraphQL()">
 | 
					<button (click)="testGraphQL()">
 | 
				
			||||||
  Test GraphQL query
 | 
					  Test GraphQL query
 | 
				
			||||||
</button>
 | 
					</button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<button (click)="testGraphQLMutation()">
 | 
				
			||||||
 | 
					  Test GraphQL mutation
 | 
				
			||||||
 | 
					</button>
 | 
				
			||||||
@ -1,8 +1,12 @@
 | 
				
			|||||||
import { Component, OnInit } from '@angular/core';
 | 
					import { Component, OnInit } from '@angular/core';
 | 
				
			||||||
import { GenericRestDataSourceService } from 'projects/poweredsoft/ngx-data/src/public-api';
 | 
					import { GenericRestDataSourceService } from 'projects/poweredsoft/ngx-data/src/public-api';
 | 
				
			||||||
import { of } from 'rxjs';
 | 
					import { of, Observable } from 'rxjs';
 | 
				
			||||||
import { DataSource } from '@poweredsoft/data';
 | 
					import { DataSource, IResolveCommandModelEvent } from '@poweredsoft/data';
 | 
				
			||||||
import { GraphQLDataSourceService } from 'projects/poweredsoft/ngx-data-apollo/src/public-api';
 | 
					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 {
 | 
					export interface IContact {
 | 
				
			||||||
@ -17,6 +21,11 @@ export interface ICustomerModel {
 | 
				
			|||||||
  lastName: string;
 | 
					  lastName: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IFooCommand {
 | 
				
			||||||
 | 
					  amount: number;
 | 
				
			||||||
 | 
					  comment: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-root',
 | 
					  selector: 'app-root',
 | 
				
			||||||
  templateUrl: './app.component.html',
 | 
					  templateUrl: './app.component.html',
 | 
				
			||||||
@ -26,7 +35,7 @@ export class AppComponent implements OnInit {
 | 
				
			|||||||
  title = 'ngx-data';
 | 
					  title = 'ngx-data';
 | 
				
			||||||
  dataSource: DataSource<ICustomerModel>;
 | 
					  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 keyResolver = (model: ICustomerModel) => model.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const transportOptions = genericService.createStandardRestTransportOptions('api/customer', keyResolver);
 | 
					    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() {
 | 
					  testGraphQL() {
 | 
				
			||||||
    const builder = this.graphQLService.createDataSourceOptionsBuilder<IContact, number>(
 | 
					    const builder = this.graphQLService.createDataSourceOptionsBuilder<IContact, number>(
 | 
				
			||||||
      'contacts',
 | 
					      'contacts',
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@ import {ApolloModule, APOLLO_OPTIONS} from 'apollo-angular';
 | 
				
			|||||||
import {HttpLinkModule, HttpLink} from 'apollo-angular-link-http';
 | 
					import {HttpLinkModule, HttpLink} from 'apollo-angular-link-http';
 | 
				
			||||||
import {InMemoryCache} from 'apollo-cache-inmemory';
 | 
					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) {
 | 
					export function createApollo(httpLink: HttpLink) {
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    link: httpLink.create({uri}),
 | 
					    link: httpLink.create({uri}),
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user