import { wrapArray } from './util' ; import { Metric , createUiStatsMetric , trackUsageAgent , UiStatsMetricType } from './metrics' ; import { Storage , ReportStorageManager } from './storage' ; import { Report , ReportManager } from './report' ; import { ApplicationUsage } from './metrics' ; export interface ReporterConfig { storage ? : Storage ; checkInterval ? : number ; debug ? : boolean ; storageKey ? : string ; } export type ReportHTTP = ( report : Report ) => Promise < void > ; export class Reporter { checkInterval : number ; private interval ? : NodeJS . Timer ; private lastAppId ? : string ; private reportManager : ReportManager ; private storageManager : ReportStorageManager ; private readonly applicationUsage : ApplicationUsage ; private debug : boolean ; private retryCount = 0 ; private readonly maxRetries = 3 ; private started = false ; constructor ( config ) { const { storage , debug , checkInterval = 90000 , storageKey = 'analytics' } = config ; this . checkInterval = checkInterval ; this . applicationUsage = new ApplicationUsage ( ) ; this . storageManager = new ReportStorageManager ( storageKey , storage ) ; const storedReport = this . storageManager . get ( ) ; this . reportManager = new ReportManager ( storedReport ) ; this . debug = ! ! debug ; } private saveToReport ( newMetrics ) { this . reportManager . assignReports ( newMetrics ) ; this . storageManager . store ( this . reportManager . report ) ; } private flushReport ( ) { this . retryCount = 0 ; this . reportManager . clearReport ( ) ; this . storageManager . store ( this . reportManager . report ) ; } public start = ( ) => { if ( ! this . interval ) { this . interval = setTimeout ( ( ) => { this . interval = undefined ; this . sendReports ( ) ; } , this . checkInterval ) ; } if ( this . started ) { return ; } if ( window && document ) { window . addEventListener ( 'beforeunload' , ( ) => this . reportApplicationUsage ( ) ) ; document . addEventListener ( 'visibilitychange' , ( ) => { if ( document . visibilityState === 'visible' && this . lastAppId ) { this . reportApplicationUsage ( this . lastAppId ) ; } else if ( document . visibilityState === 'hidden' ) { this . reportApplicationUsage ( ) ; this . sendReports ( ) ; } } ) ; } this . started = true ; this . applicationUsage . start ( ) ; } ; private log ( message ) { if ( this . debug ) { console . debug ( message ) ; } } public reportUiStats = ( appName , type , eventNames , count ? ) => { const metrics = wrapArray ( eventNames ) . map ( ( eventName ) => { this . log ( `${ type } Metric -> (${ appName }:${ eventName }):` ) ; const report = createUiStatsMetric ( { type , appName , eventName , count } ) ; this . log ( report ) ; return report ; } ) ; this . saveToReport ( metrics ) ; } ; public reportUserAgent = ( appName ) => { this . log ( `Reporting user-agent.` ) ; const report = trackUsageAgent ( appName ) ; this . saveToReport ( [ report ] ) ; } ; public reportApplicationUsage ( appId ? ) { this . log ( `Reporting application changed to ${ appId }` ) ; this . lastAppId = appId || this . lastAppId ; } public sendReports = async ( ) => { if ( ! this . reportManager . isReportEmpty ( ) ) { try { this . flushReport ( ) ; } catch ( err ) { this . log ( `Error Sending Metrics Report ${ err }` ) ; this . retryCount = this . retryCount + 1 ; const versionMismatch = this . reportManager . report . reportVersion !== ReportManager . REPORT_VERSION ; if ( versionMismatch || this . retryCount > this . maxRetries ) { this . flushReport ( ) ; } } } this . start ( ) ; } ; }