import { HttpClient , HttpErrorResponse , HttpHeaders , HttpParams , HttpResponse } from '@angular/common/http' ; import { EventEmitter } from '@angular/core' ; import { process , State } from '@progress/kendo-data-query' ; import { DataServiceEvent } from './data-service.event' ; import { DataServiceInterface } from './data-service.interface' ; import { ModelDataResult } from './model-data-result' ; import { DataProviderService , DataServiceConfig , DataServiceRequest } from './service-config' ; import { Observable , BehaviorSubject } from 'rxjs' ; import { tap , map } from 'rxjs/operators' ; export abstract class DataService < T > implements DataServiceInterface < T > { public state : State ; public readonly errors = new BehaviorSubject < Error > ( null ) ; public readonly events = new EventEmitter < DataServiceEvent > ( ) ; public pendingData : boolean ; protected dataResult : ModelDataResult < T > ; private shouldFetch = true ; private dataStream : BehaviorSubject < ModelDataResult < T > > = new BehaviorSubject < ModelDataResult < T > > ( null ) ; constructor ( protected config , protected http , protected dataProviderService , protected initialState = { } ) { } public dataChanges ( ) { return this . dataStream ; } public read ( state ? ) { if ( state ) { this . state = state ; } else { this . state = this . initialState ; } if ( ! this . config . serverOperations && this . dataResult && this . dataResult . data . length ) { const currentData = process ( this . dataResult . data , this . state ) ; return this . dataStream . next ( currentData ) ; } if ( this . shouldFetch ) { this . shouldFetch = false ; this . pendingData = true ; const requestState = this . config . serverOperations ? this . state : { } ; this . readRequest ( requestState ) . pipe ( map ( response => this . parseResponse ( response ) ) , tap ( data => this . dataResult = data ) ) . subscribe ( data => { this . shouldFetch = true ; this . pendingData = false ; if ( ! this . config . serverOperations ) { data = process ( data . data , this . state ) ; } this . dataStream . next ( data ) ; } , ( err ) => { this . shouldFetch = true ; this . pendingData = false ; this . handleError ( err ) ; this . dataStream . next ( this . dataResult || { data : [ ] , total : 0 } ) ; } ) ; } } public create ( item ) { this . handleRequest ( this . createRequest ( item ) , { action : 'create' } ) ; } public update ( item ) { this . handleRequest ( this . updateRequest ( item ) , { action : 'update' } ) ; } public remove ( item ) { this . handleRequest ( this . removeRequest ( item ) , { action : 'remove' } ) ; } public batch ( deletedItems , createdItems , updatedItems ) { deletedItems . forEach ( item => { this . remove ( item ) ; } ) ; updatedItems . forEach ( item => { this . update ( item ) ; } ) ; createdItems . forEach ( item => { this . create ( item ) ; } ) ; } public fetchedData ( ) { return this . dataResult ; } public reset ( ) { this . dataResult = null ; } public createModel ( ) { return this . config . createModel ( ) ; } protected request ( method , url , options ) { options . headers = options . headers || new HttpHeaders ( ) ; return this . http . request ( method , url , options ) ; } protected handleRequest ( request , event ) { this . reset ( ) ; request . subscribe ( ( ) => { this . read ( this . state ) ; this . events . emit ( event ) ; } , ( err ) => { this . handleError ( err ) ; } ) ; } protected abstract readRequest ( state ) ; protected abstract createRequest ( data ) ; protected abstract updateRequest ( data ) ; protected abstract removeRequest ( data ) ; protected abstract parseResponse ( response ) ; protected getAbsoluteUrl ( request ) { const dataProvider = this . dataProviderService . get ( this . config . dataProviderName ) ; let serviceUri = dataProvider ? dataProvider . serviceUri : '' ; serviceUri = serviceUri . replace ( /[/]$/ , '' ) + '/' ; const resourceUrl = Object . keys ( request . routeParams || { } ) . reduce ( ( prev , current ) => { const regEx = new RegExp ( ':' + current , 'gi' ) ; return prev . replace ( regEx , request . routeParams [ current ] ) ; } , request . url ) ; const queryString = request . queryString && request . queryString . length ? '?' + request . queryString : '' ; return `${ serviceUri }${ resourceUrl }${ queryString }` ; } protected getQueryString ( state ) { return '' ; } protected mapData ( data ) { if ( this . config . mapData ) { return data . map ( item => this . config . mapData ( item ) ) ; } return data ; } protected handleError ( err ) { this . errors . next ( err ) ; } }