import { App } from './../app' ; import { StatsDriver } from './stats-driver' ; import * as dot from 'dot-wild' ; const dayjs = require ( 'dayjs' ) ; export class LocalStats implements StatsDriver { type { any } protected stats : any = { } ; type { any } protected snapshots : any = { } ; type { string [ ] } protected registeredApps : string [ ] = [ ] ; param { any } options constructor ( protected options ) { } param { App } app return { Promise < number > } markNewConnection ( app ) { if ( ! this . canRegisterStats ( app ) ) { return new Promise ( resolve => resolve ( 0 ) ) ; } return this . registerApp ( app ) . then ( ( ) => { return this . increment ( app , 'connections' ) . then ( connections => { let peakConnections = Math . max ( this . stats [ app . key ] [ 'peak_connections' ] || 0 , connections ) ; return this . set ( app , 'peak_connections' , peakConnections ) . then ( ( ) => connections ) ; } ) ; } ) ; } param { App } app param { string | null } reason return { Promise < number > } markDisconnection ( app , reason ? ) { if ( ! this . canRegisterStats ( app ) ) { return new Promise ( resolve => resolve ( 0 ) ) ; } return this . registerApp ( app ) . then ( ( ) => { return this . decrement ( app , 'connections' ) ; } ) ; } param { App } app return { Promise < number > } markApiMessage ( app ) { if ( ! this . canRegisterStats ( app ) ) { return new Promise ( resolve => resolve ( 0 ) ) ; } return this . registerApp ( app ) . then ( ( ) => { return this . increment ( app , 'api_messages' ) ; } ) ; } param { App } app return { Promise < number > } markWsMessage ( app ) { if ( ! this . canRegisterStats ( app ) ) { return new Promise ( resolve => resolve ( 0 ) ) ; } return this . registerApp ( app ) . then ( ( ) => { return this . increment ( app , 'ws_messages' ) ; } ) ; } param { App | string } app return { Promise < any > } getStats ( app ) { let appKey = app instanceof App ? app . key : app ; return new Promise ( resolve => { resolve ( { connections : this . stats [ appKey ] ? ( this . stats [ appKey ] [ 'connections' ] || 0 ) : 0 , peak_connections : this . stats [ appKey ] ? ( this . stats [ appKey ] [ 'peak_connections' ] || 0 ) : 0 , api_messages : this . stats [ appKey ] ? ( this . stats [ appKey ] [ 'api_messages' ] || 0 ) : 0 , ws_messages : this . stats [ appKey ] ? ( this . stats [ appKey ] [ 'ws_messages' ] || 0 ) : 0 , } ) ; } ) ; } param { App | string } app param { number | null } time return { Promise < any > } takeSnapshot ( app , time ? ) { let appKey = app instanceof App ? app . key : app ; if ( ! this . snapshots [ appKey ] ) { this . snapshots [ appKey ] = [ ] ; } return this . getStats ( app ) . then ( stats => { let record = { time , stats , } ; this . snapshots [ appKey ] . push ( record ) ; return record ; } ) . then ( record => { return this . hasActivity ( app ) . then ( hasActivity => { if ( ! hasActivity ) { return this . resetAppTraces ( app ) . then ( ( ) => record ) ; } /**
                 * Reset the messages stats to 0 only
                 * if the app still has activity.
                 */ this . resetMessagesStats ( app ) ; return record ; } ) ; } ) ; } param { App | string } app param { number | null } start param { number | null } end return { Promise < any > } getSnapshots ( app , start ? , end ? ) { let appKey = app instanceof App ? app . key : app ; start = start ? start : dayjs ( ) . subtract ( 7 , 'day' ) . unix ( ) ; end = end ? end : dayjs ( ) . unix ( ) ; return new Promise ( resolve => resolve ( ( this . snapshots [ appKey ] || [ ] ) . filter ( point => start <= point . time && point . time <= end ) ) ) ; } param { App | string } app param { number | null } time return { Promise < boolean > } deleteStalePoints ( app , time ? ) { let appKey = app instanceof App ? app . key : app ; return this . getSnapshots ( app , 0 , Infinity ) . then ( snapshots => { return this . snapshots [ appKey ] = snapshots . filter ( point => { return point . time >= ( time - this . options . stats . retention . period ) ; } ) ; } ) . then ( ( ) => true ) ; } param { App | string } app return { Promise < boolean > } registerApp ( app ) { let appKey = app instanceof App ? app . key : app ; return new Promise ( resolve => { if ( this . registeredApps . indexOf ( appKey ) === - 1 ) { this . registeredApps . push ( appKey ) ; } resolve ( true ) ; } ) ; } return { Promise < string [ ] > } getRegisteredApps ( ) { return new Promise ( resolve => resolve ( this . registeredApps ) ) ; } param { App } app param { string } stat return { Promise < number > } protected increment ( app , stat ) { return new Promise ( resolve => { if ( ! this . canRegisterStats ( app ) ) { return resolve ( 0 ) ; } if ( dot . has ( this . stats , `${ app . key }.${ stat }` ) ) { this . stats [ app . key ] [ stat ] ++ ; resolve ( this . stats [ app . key ] [ stat ] ) ; } else { this . stats = dot . set ( this . stats , `${ app . key }.${ stat }` , 1 ) ; resolve ( 1 ) ; } } ) ; } param { App } app param { string } stat return { Promise < number > } protected decrement ( app , stat ) { return new Promise ( resolve => { if ( ! this . canRegisterStats ( app ) ) { return resolve ( 0 ) ; } if ( dot . has ( this . stats , `${ app . key }.${ stat }` ) && this . stats [ app . key ] [ stat ] > 0 ) { this . stats [ app . key ] [ stat ] -- ; resolve ( this . stats [ app . key ] [ stat ] ) ; } else { this . stats = dot . set ( this . stats , `${ app . key }.${ stat }` , 0 ) ; resolve ( 0 ) ; } } ) ; } param { App } app param { string } stat param { number } value return { Promise < number > } protected set ( app , stat , value ) { return new Promise ( resolve => { if ( ! this . canRegisterStats ( app ) ) { return resolve ( 0 ) ; } this . stats [ app . key ] [ stat ] = value ; resolve ( value ) ; } ) ; } param { App | string } app return { void } protected resetMessagesStats ( app ) { let appKey = app instanceof App ? app . key : app ; this . stats [ appKey ] [ 'api_messages' ] = 0 ; this . stats [ appKey ] [ 'ws_messages' ] = 0 ; this . stats [ appKey ] [ 'peak_connections' ] = this . stats [ appKey ] [ 'connections' ] || 0 ; } param { App | string } app return { Promise < boolean > } protected resetAppTraces ( app ) { return new Promise ( resolve => { let appKey = app instanceof App ? app . key : app ; delete this . stats [ appKey ] ; delete this . snapshots [ appKey ] ; this . registeredApps . splice ( this . registeredApps . indexOf ( appKey ) , 1 ) ; resolve ( true ) ; } ) ; } param { App | string } app return { Promise < boolean > } protected hasActivity ( app ) { return this . getSnapshots ( app , 0 , Infinity ) . then ( snapshots => { let activeSnapshots = snapshots . filter ( point => { /**
                 * Check if the sum of all stats are > 0.
                 * If it is, it means that the app still has activity.
                 */ return Object . values ( point . stats ) . reduce ( ( sum , num ) => sum += num , 0 ) > 0 ; } ) ; return activeSnapshots . length > 0 ; } ) ; } param { App } app return { boolean } protected canRegisterStats ( app ) { return this . options . stats . enabled && ! ! app . enableStats ; } }