import { IModel , Model } from 'mobx-collection-store' ; import IDictionary from './interfaces/IDictionary' ; import IRequestOptions from './interfaces/IRequestOptions' ; import * as JsonApi from './interfaces/JsonApi' ; import { buildUrl , create , fetchLink , handleResponse , remove , update } from './NetworkUtils' ; import { Response } from './Response' ; import { Store } from './Store' ; import { getValue , mapItems , objectForEach } from './utils' ; interface IInternal { relationships ? : IDictionary < JsonApi . IRelationship > ; meta ? : object ; links ? : IDictionary < JsonApi . ILink > ; persisted ? : boolean ; id : number | string ; type : string ; } export class Record extends Model implements IModel { static memberOf public static typeAttribute = [ '__internal' , 'type' ] ; static memberOf public static idAttribute = [ '__internal' , 'id' ] ; static type { boolean } memberOf public static useAutogeneratedIds : boolean = false ; static type { string | ( ) => string } memberOf public static endpoint : string | ( ( ) => string ) ; public 'static' : typeof Record ; private type { IInternal } memberOf private __internal : IInternal ; private type { IDictionary < Promise < Response > > } memberOf private __relationshipLinkCache : IDictionary < IDictionary < Promise < Response > > > = { } ; private type { IDictionary < Promise < Response > > } memberOf private __linkCache : IDictionary < Promise < Response > > = { } ; returns { IDictionary < JsonApi . IRelationship > } memberOf public getRelationshipLinks ( ) { return this . __internal && this . __internal . relationships ; } param { string } relationship param { string } name param { IRequestOptions } options param { boolean } force returns { Promise < Response > } memberOf public fetchRelationshipLink ( relationship , name , options ? , force = false ) { this . __relationshipLinkCache [ relationship ] = this . __relationshipLinkCache [ relationship ] || { } ; if ( ! ( name in this . __relationshipLinkCache ) || force ) { const link = ( 'relationships' in this . __internal && relationship in this . __internal . relationships && name in this . __internal . relationships [ relationship ] ) ? this . __internal . relationships [ relationship ] [ name ] : null ; const headers = options && options . headers ; this . __relationshipLinkCache [ relationship ] [ name ] = fetchLink ( link , this . __collection as Store , headers , options ) ; } return this . __relationshipLinkCache [ relationship ] [ name ] ; } returns { object } memberOf public getMeta ( ) { return this . __internal && this . __internal . meta ; } returns { IDictionary < JsonApi . ILink > } memberOf public getLinks ( ) { return this . __internal && this . __internal . links ; } param { string } name param { IRequestOptions } options param { boolean } force returns { Promise < Response > } memberOf public fetchLink ( name , options ? , force = false ) { if ( ! ( name in this . __linkCache ) || force ) { const link = ( 'links' in this . __internal && name in this . __internal . links ) ? this . __internal . links [ name ] : null ; this . __linkCache [ name ] = fetchLink ( link , this . __collection as Store , options && options . headers , options ) ; } let request = this . __linkCache [ name ] ; if ( this [ '__queue__' ] ) { request = this . __linkCache [ name ] . then ( ( response ) => { const related = this [ '__related__' ] ; const prop = this [ '__prop__' ] ; const record = response . data as Record ; if ( record && record . getRecordType ( ) !== this . getRecordType ( ) && record . getRecordType ( ) === related . getRecordType ( ) ) { if ( prop ) { related [ prop ] = record ; return response ; } related . __persisted = true ; return response . replaceData ( related ) ; } return response ; } ) ; } return request ; } readonly private type { boolean } memberOf private get __persisted ( ) : boolean { return ( this . __internal && this . __internal . persisted ) || false ; } private memberOf private set __persisted ( state ) { this . __internal . persisted = state ; } returns { JsonApi . IRecord } memberOf public toJsonApi ( ) { const attributes = this . toJS ( ) ; const useAutogenerated = this . static [ 'useAutogeneratedIds' ] ; const data = { attributes , id : ( this . __persisted || useAutogenerated ) ? this . getRecordId ( ) : undefined , type : this . getRecordType ( ) as string , } ; const refs = this [ '__refs' ] ; objectForEach ( refs , ( key ) => { data . relationships = data . relationships || { } ; const rel = mapItems ( this [ `${ key }Id` ] , ( id ) => { if ( ! id && id !== 0 ) { return null ; } return { id , type : refs [ key ] } ; } ) ; data . relationships [ key ] = { data : rel } as JsonApi . IRelationship ; delete data . attributes [ key ] ; delete data . attributes [ `${ key }Id` ] ; delete data . attributes [ `${ key }Meta` ] ; } ) ; delete data . attributes . __internal ; delete data . attributes . __type__ ; return data ; } param { IRequestOptions } options param { boolean } ignoreSelf returns { Promise < Record > } memberOf public save ( options ? , ignoreSelf = false ) { const store = this . __collection as Store ; const data = this . toJsonApi ( ) ; const requestMethod = this . __persisted ? update : create ; return requestMethod ( store , this . __getUrl ( options , ignoreSelf ) , { data } , options && options . headers ) . then ( handleResponse ( this ) ) ; } public saveRelationship ( relationship , options ? ) { const link = ( 'relationships' in this . __internal && relationship in this . __internal . relationships && 'self' in this . __internal . relationships [ relationship ] ) ? this . __internal . relationships [ relationship ] [ 'self' ] : null ; if ( ! link ) { throw new Error ( 'The relationship doesn\'t have a defined link' ) ; } const store = this . __collection as Store ; const href = typeof link === 'object' ? link . href : link ; const type = this [ '__refs' ] [ relationship ] ; type ID = JsonApi . IIdentifier | Array < JsonApi . IIdentifier > ; const data = mapItems ( this [ `${ relationship }Id` ] , ( id ) => ( { id , type } ) ) as ID ; return update ( store , href , { data } , options && options . headers ) . then ( handleResponse ( this , relationship ) ) ; } param { IRequestOptions } options param { boolean } ignoreSelf returns { Promise < boolean > } memberOf public remove ( options ? , ignoreSelf = false ) { const store = this . __collection as Store ; if ( ! this . __persisted ) { this . __collection . remove ( this . getRecordType ( ) , this . getRecordId ( ) ) ; return Promise . resolve ( true ) ; } return remove ( store , this . __getUrl ( options , ignoreSelf ) , options && options . headers ) . then ( ( response ) => { if ( response . error ) { throw response . error ; } this . __persisted = false ; if ( this . __collection ) { this . __collection . remove ( this . getRecordType ( ) , this . getRecordId ( ) ) ; } return true ; } ) ; } param { boolean } state memberOf public setPersisted ( state ) { this . __persisted = state ; } memberOf public getPersisted ( ) { return this . __persisted ; } private returns { string } memberOf private __getUrl ( options ? , ignoreSelf ? ) { const links = this . getLinks ( ) ; if ( ! ignoreSelf && links && links . self ) { const self = links . self ; return typeof self === 'string' ? self : self . href ; } const type = getValue < string > ( this . static . endpoint ) || this . getRecordType ( ) || this . static . type ; return buildUrl ( type , this . __persisted ? this . getRecordId ( ) : null , null , options ) ; } }