diff --git a/projects/poweredsoft/ngx-data/package.json b/projects/poweredsoft/ngx-data/package.json index 3626e3f..bb816c1 100644 --- a/projects/poweredsoft/ngx-data/package.json +++ b/projects/poweredsoft/ngx-data/package.json @@ -1,6 +1,6 @@ { "name": "@poweredsoft/ngx-data", - "version": "0.0.13", + "version": "0.0.14", "peerDependencies": { "@angular/common": "^8.2.4", "@angular/core": "^8.2.4", diff --git a/projects/poweredsoft/ngx-data/src/lib/HttpDataSourceBuilder.ts b/projects/poweredsoft/ngx-data/src/lib/HttpDataSourceBuilder.ts new file mode 100644 index 0000000..35f18cf --- /dev/null +++ b/projects/poweredsoft/ngx-data/src/lib/HttpDataSourceBuilder.ts @@ -0,0 +1,167 @@ +import { HttpClient, HttpErrorResponse } from "@angular/common/http"; +import { DataSource, IDataSource, IDataSourceCommandAdapterOptions, IDataSourceError, IDataSourceErrorMessage, IDataSourceOptions, IDataSourceQueryAdapterOptions, IDataSourceTransportOptions, IDataSourceValidationError, IQueryCriteria, IQueryExecutionGroupResult, IQueryExecutionResult, IResolveCommandModelEvent } from "@poweredsoft/data"; +import { Observable, of, throwError } from "rxjs"; +import { catchError, switchMap } from "rxjs/operators"; + + + +export class HttpDataSourceOptionsBuilder { + + private _commands: { [key: string] : IDataSourceCommandAdapterOptions } = {}; + private _beforeRead: (TQuery: IQueryCriteria) => Observable; + private _keyResolver: (model: TModel) => TKey; + private _defaultCriteria: IQueryCriteria; + private _query: IDataSourceQueryAdapterOptions; + private _manageNotificationMessage: boolean = false; + + constructor(private http: HttpClient) { + + } + + createOptions(): IDataSourceOptions { + let ret: IDataSourceOptions = { + resolveIdField: this._keyResolver, + defaultCriteria: this._defaultCriteria, + manageNotificationMessage: this._manageNotificationMessage, + transport: this.createTransport() + }; + return ret; + } + + manageNotificationMessage(shouldManage: boolean) { + this._manageNotificationMessage = shouldManage; + return this; + } + + createDataSource() : IDataSource{ + return new DataSource(this.createOptions()); + } + + protected createTransport(): IDataSourceTransportOptions { + let ret: IDataSourceTransportOptions = { + query: this._query, + commands: this._commands + }; + return ret; + } + + public keyResolver(resolver: (model: TModel) => TKey) { + this._keyResolver = resolver; + return this; + } + + public beforeRead(beforeRead: (query: TDynamicQuery) => Observable) + { + this._beforeRead = beforeRead; + return this; + } + + public queryUrl(url: string) { + this._query = { + adapter: { + handle: (query: IQueryCriteria) => { + const finalBeforeRead = this._beforeRead || (t => of(query)); + return finalBeforeRead(query) + .pipe( + switchMap(finalQuery => { + return this.http.post & IQueryExecutionGroupResult>(url, finalQuery); + }) + ); + } + } + } + + return this; + } + + public queryHandler(queryHandler: (query: TQuery) => Observable & IQueryExecutionGroupResult>) { + this._query = { + adapter: { + handle: (query: TQuery) => { + const finalBeforeRead = this._beforeRead || (t => of(query)); + return finalBeforeRead(query) + .pipe( + switchMap(finalQuery => { + return queryHandler(finalQuery as any); + }) + ); + } + } + } + + return this; + } + + private _handleErrorPipe(err: HttpErrorResponse) : Observable { + + if (err.status == 500) { + return throwError({ + type: 'message', + message: 'UNEXPECTED_ERROR_MESSAGE' + }); + } + + if (typeof(err.error) == "object") { + // if status not okay then its an exception error + if (err.error.hasOwnProperty('Message') && typeof(err.error['Message']) == "string") { + return throwError({ + type: 'message', + message: err.error['Message'] + }); + } + + return throwError({ + type: 'validation', + errors: err.error + }); + } + + // general error message + if (typeof(err.error) == "string") { + return throwError({ + type: 'message', + message: err.error + }); + } + + return throwError({ + type: 'message', + message: 'UNEXPECTED_ERROR_MESSAGE' + }); + } + + public addCommandByCallback(name: string, commandHandler: (command: TCommand) => Observable, resolveCommandModel?: (event: IResolveCommandModelEvent) => Observable) { + const handleWrapper = command => { + return commandHandler(command).pipe(catchError(this._handleErrorPipe)); + }; + + this._commands[name] = > { + adapter: { + handle: handleWrapper + }, + resolveCommandModel: resolveCommandModel + }; + + return this; + } + + public addCommandByUrl(name: string, url: string, resolveCommandModel?: (event: IResolveCommandModelEvent) => Observable) { + const handleWrapper = command => { + return this.http.post(url, command).pipe(catchError(this._handleErrorPipe)); + }; + + this._commands[name] = > { + adapter: { + handle: handleWrapper + }, + resolveCommandModel: resolveCommandModel + }; + + return this; + } + + defaultCriteria(criteria: IQueryCriteria) { + this._defaultCriteria = criteria; + return this; + } +} diff --git a/projects/poweredsoft/ngx-data/src/lib/http-data-source-service.service.ts b/projects/poweredsoft/ngx-data/src/lib/http-data-source-service.service.ts new file mode 100644 index 0000000..0970088 --- /dev/null +++ b/projects/poweredsoft/ngx-data/src/lib/http-data-source-service.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from "@angular/core"; +import { HttpClient } from '@angular/common/http'; +import { HttpDataSourceOptionsBuilder } from "./HttpDataSourceBuilder"; + + +@Injectable({ + providedIn: 'root' +}) +export class HttpDataSourceService { + constructor(private http: HttpClient) { + } + + builder() { + return new HttpDataSourceOptionsBuilder(this.http); + } +} diff --git a/projects/poweredsoft/ngx-data/src/public-api.ts b/projects/poweredsoft/ngx-data/src/public-api.ts index a96d7ff..cf7c152 100644 --- a/projects/poweredsoft/ngx-data/src/public-api.ts +++ b/projects/poweredsoft/ngx-data/src/public-api.ts @@ -3,4 +3,5 @@ */ export * from './lib/ngx-data.service'; +export * from './lib/http-data-source-service.service' export * from './lib/ngx-data.module'; diff --git a/src/app/app.component.html b/src/app/app.component.html index 9e6068b..bf4b4e7 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,3 +1,13 @@ - \ No newline at end of file + + + + +

Latest Data

+
+
+  {{ latestData | json }}
+
\ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 655e336..5c25a2f 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { GenericRestDataSourceService } from 'projects/poweredsoft/ngx-data/src/public-api'; import { of, Observable } from 'rxjs'; -import { DataSource, IResolveCommandModelEvent } from '@poweredsoft/data'; +import { DataSource, IDataSource, IResolveCommandModelEvent } from '@poweredsoft/data'; import { } from 'projects/poweredsoft/ngx-data-apollo/src/public-api'; import { Apollo } from 'apollo-angular'; import gql from 'graphql-tag'; @@ -9,28 +9,21 @@ import { map } from 'rxjs/operators'; import { DocumentNode } from 'graphql'; import { GraphQLDataSourceService, IGraphQLAdvanceQueryInput } from 'projects/poweredsoft/ngx-data-apollo/src/public-api'; import { TestService, ITestModel, IValidationTestCommand } from './services/test.service'; +import { HttpDataSourceService} from '@poweredsoft/ngx-data'; - -export interface IContact { - id: number; - firstName :string; - lastName: string; -} - -export interface IContactModel { - id: number; - firstName: string; - lastName: string; -} - -export interface IFooCommand { - amount: number; - comment: string; -} - -export interface IContactDetailQuery extends IGraphQLAdvanceQueryInput +export class IContact { - sex?: string; + id: number + displayName: string +} + +export interface ICreatePerson { + firstName: string + lastName: string +} + +export interface IEchoCommand { + message: string } @Component({ @@ -40,32 +33,49 @@ export interface IContactDetailQuery extends IGraphQLAdvanceQueryInput; + dataSource: IDataSource; + latestData: any; - constructor(private testService: TestService) { - this.dataSource = testService.generateDatasource({ - criteria: { + constructor(private hdss: HttpDataSourceService) { + const ds = hdss + .builder() + .keyResolver(m => m.id) + .defaultCriteria({ page: 1, - pageSize: 10 - } - }); + pageSize: 5 + }) + .queryUrl('https://localhost:5001/api/query/contacts') + .addCommandByUrl("createPerson", 'https://localhost:5001/api/command/createPerson', + e => { + return of ({ + firstName: '', + lastName: '' + }) + }) + .addCommandByUrl('echo', 'https://localhost:5001/api/command/echo') + .createDataSource(); + + this.dataSource = ds; } ngOnInit(): void { - this.dataSource.notifyMessage$.subscribe((notification) => { - console.log('notifcation', notification); - }); - this.dataSource.validationError$.subscribe((notification) => { - console.log('error', notification); + this.dataSource.data$.subscribe(newData => { + this.latestData = newData; }); } - testValidation() { - this.dataSource.executeCommandByName('validationTest', { - value: 'test' - }).subscribe((result) => { - console.log(result); - }); + refresh() { + this.dataSource.refresh(); + } + + echoCommand() { + const message = prompt('What message you wish to echo? '); + this.dataSource.executeCommandByName('echo', { + message: message + }).subscribe( + commandResult => alert(commandResult), + err => console.log(err) + ); } }