diff --git a/package.json b/package.json index c198897..02c691d 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/projects/poweredsoft/ngx-data-apollo/package.json b/projects/poweredsoft/ngx-data-apollo/package.json index b648682..96e7143 100644 --- a/projects/poweredsoft/ngx-data-apollo/package.json +++ b/projects/poweredsoft/ngx-data-apollo/package.json @@ -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", diff --git a/projects/poweredsoft/ngx-data-apollo/src/lib/GraphQLDataSourceOptionsBuilder.ts b/projects/poweredsoft/ngx-data-apollo/src/lib/GraphQLDataSourceOptionsBuilder.ts index 860d195..f84a6fa 100644 --- a/projects/poweredsoft/ngx-data-apollo/src/lib/GraphQLDataSourceOptionsBuilder.ts +++ b/projects/poweredsoft/ngx-data-apollo/src/lib/GraphQLDataSourceOptionsBuilder.ts @@ -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 { - - private _commands: { [key: string] : IDataSourceCommandAdapterOptions }; + querySelect: string; + private _commands: { [key: string] : IDataSourceCommandAdapterOptions } = {}; 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 { let ret: IDataSourceOptions = { resolveIdField: this.keyResolver, @@ -28,6 +36,7 @@ export class GraphQLDataSourceOptionsBuilder { }; return ret; } + protected createTransport(): IDataSourceTransportOptions { let ret: IDataSourceTransportOptions = { query: this.createQuery(), @@ -122,6 +131,7 @@ export class GraphQLDataSourceOptionsBuilder { return null; } + private createGraphQLQuery(query: IQueryCriteria): DocumentNode { return gql` query getAll($criteria: ${this.queryInputName}) { @@ -163,14 +173,63 @@ export class GraphQLDataSourceOptionsBuilder { groups: query.groups ? query.groups.map(this.convertGroup.bind(this)) : null }; return ret; + } - - public addMutation(name: string, handle: (command: TMutation) => Observable) { - - this._commands[name] = > { + + /*public addMutationTest(name: string, mutationName: string, mutationSelect?: string, resolveCommandModel?: (event: IResolveCommandModelEvent) => Observable) { + this._commands[name] = > { adapter: { - handle: handle - } + handle: this.apollo.use() + }, + resolveCommandModel: resolveCommandModel + }; + + return this; + }*/ + + public addMutation(name: string, mutationName: string, handle: (command: TMutation) => Observable>, resolveCommandModel?: (event: IResolveCommandModelEvent) => Observable) { + 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({ + type: 'validation', + errors: result + }); + } + } + + return throwError({ + type: 'message', + message: apolloError.message + }); + }) + ); + }; + + this._commands[name] = > { + adapter: { + handle: handleWrapper + }, + resolveCommandModel: resolveCommandModel }; return this; diff --git a/src/app/app.component.html b/src/app/app.component.html index 392f50d..fcc9fe2 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -8,4 +8,8 @@ + + \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 50732bd..82cfdb8 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -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; - 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( + 'contacts', + 'GraphQLAdvanceQueryOfContactModelInput', + 'id firstName lastName', + (m) => m.id, + { + groups: [ + { + path: 'sex' + } + ], + aggregates: [ + { + path: 'id', + type: 'Max' + } + ] + } + ); + + builder.addMutation('create', 'foo', (command) => { + return this.apollo.mutate({ + 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(dataSourceOptions); + let event: IResolveCommandModelEvent = { + 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( 'contacts', diff --git a/src/app/graphql.module.ts b/src/app/graphql.module.ts index 7cd8b75..c3f8cb9 100644 --- a/src/app/graphql.module.ts +++ b/src/app/graphql.module.ts @@ -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}),