import { action } from 'mobx' ; import { Collection , IModel } from 'mobx-collection-store' ; import ICache from './interfaces/ICache' ; import IDictionary from './interfaces/IDictionary' ; import IHeaders from './interfaces/IHeaders' ; import IRequestOptions from './interfaces/IRequestOptions' ; import * as JsonApi from './interfaces/JsonApi' ; import { NetworkStore } from './NetworkStore' ; import { fetch , prefixUrl , read } from './NetworkUtils' ; import { Record } from './Record' ; import { Response } from './Response' ; import { flattenRecord , mapItems } from './utils' ; interface IQueryParams { url : string ; data ? : object ; headers : IHeaders ; } export class Store extends NetworkStore { static memberOf public static types = [ Record ] ; static memberof public static cache = true ; public static : typeof Store ; private memberOf private __cache : ICache = { fetch : { } , fetchAll : { } , } ; param { IJsonApiResponse } body returns { ( IModel | Array < IModel > ) } memberOf @ action public sync ( body ) { const data = this . __iterateEntries ( body , this . __addRecord . bind ( this ) ) ; this . __iterateEntries ( body , this . __updateRelationships . bind ( this ) ) ; return data ; } param { string } type param { number | string } type param { boolean } force param { IRequestOptions } options returns { Promise < Response > } memberOf public fetch ( type , id , force ? , options ? ) { const query = this . __prepareQuery ( type , id , null , options ) ; if ( ! this . static . cache ) { return this . __doFetch ( query , options ) ; } this . __cache . fetch [ type ] = this . __cache . fetch [ type ] || { } ; if ( force || ! ( query . url in this . __cache . fetch [ type ] ) ) { this . __cache . fetch [ type ] [ query . url ] = this . __doFetch ( query , options ) . catch ( ( e ) => { delete this . __cache . fetch [ type ] [ query . url ] ; throw e ; } ) ; } return this . __cache . fetch [ type ] [ query . url ] ; } param { string } type param { boolean } force param { IRequestOptions } options returns { Promise < Response > } memberOf public fetchAll ( type , force ? , options ? ) { const query = this . __prepareQuery ( type , null , null , options ) ; if ( ! this . static . cache ) { return this . __doFetch ( query , options ) ; } this . __cache . fetchAll [ type ] = this . __cache . fetchAll [ type ] || { } ; if ( force || ! ( query . url in this . __cache . fetchAll [ type ] ) ) { this . __cache . fetchAll [ type ] [ query . url ] = this . __doFetch ( query , options ) . catch ( ( e ) => { delete this . __cache . fetchAll [ type ] [ query . url ] ; throw e ; } ) ; } return this . __cache . fetchAll [ type ] [ query . url ] ; } param { string } type param { ( number | string ) } id param { IRequestOptions } options returns { Promise < boolean > } memberOf public destroy ( type , id , options ? ) { const model = this . find ( type , id ) as Record ; if ( model ) { return model . remove ( options ) ; } return Promise . resolve ( true ) ; } public reset ( ) { super . reset ( ) ; this . __cache . fetch = { } ; this . __cache . fetchAll = { } ; } public request ( url , method = 'GET' , data ? , options ? ) { return fetch ( { url : prefixUrl ( url ) , options , data , method , store : this } ) ; } public removeAll < T extends IModel > ( type ) { const models = super . removeAll < T > ( type ) ; this . __cache . fetch [ type ] = { } ; this . __cache . fetchAll [ type ] = { } ; return models ; } param { IQueryParams } query param { IRequestOptions } options returns { Promise < Response > } memberof private __doFetch ( query , options ? ) { return read ( this , query . url , query . headers , options ) . then ( this . __handleErrors ) ; } private param { Response } response returns memberOf private __handleErrors ( response ) { if ( response . error ) { throw response . error ; } return response ; } private param { IJsonApiRecord } obj returns { IModel } memberOf private __addRecord ( obj ) { const { type , id } = obj ; let record = this . find ( type , id ) as Record ; const flattened = flattenRecord ( obj ) ; if ( record ) { record . update ( flattened ) ; } else if ( this . static . types . filter ( ( item ) => item . type === obj . type ) . length ) { record = this . add ( flattened , obj . type ) as Record ; } else { record = new Record ( flattened ) ; this . add ( record ) ; } if ( record && typeof record . setPersisted === 'function' ) { record . setPersisted ( true ) ; } return record ; } private param { IJsonApiRecord } obj returns { void } memberOf private __updateRelationships ( obj ) { const record = this . find ( obj . type , obj . id ) ; const refs = obj . relationships ? Object . keys ( obj . relationships ) : [ ] ; refs . forEach ( ( ref ) => { if ( ! ( 'data' in obj . relationships [ ref ] ) ) { return ; } const items = obj . relationships [ ref ] . data ; if ( items instanceof Array && items . length < 1 ) { if ( ! ( ref in record ) || ref in record [ '__data' ] ) { record . assignRef ( ref , [ ] ) ; } } else if ( record ) { if ( items ) { const models = mapItems < IModel > ( items , ( { id , type } ) => this . find ( type , id ) || id ) ; const itemType = items instanceof Array ? items [ 0 ] . type : items . type ; record . assignRef ( ref , models , itemType ) ; } else { record . assignRef ( ref , null ) ; } } } ) ; } private param { IJsonApiResponse } body param { Function } fn returns memberOf private __iterateEntries ( body , fn ) { mapItems ( ( body && body . included ) || [ ] , fn ) ; return mapItems < IModel > ( ( body && body . data ) || [ ] , fn ) ; } }