@benshi.ai/js-sdk
Version:
Benshi SDK
170 lines (136 loc) • 5.22 kB
text/typescript
import {NetworkProxy} from '../drivers/network/typings'
import {debug, Debug} from '../logging'
import {v4 as uuidv4} from 'uuid';
import {AppInfoObject, DeviceInfoObject, Event, EventMainObject, LogIngestorOptions, QueueItem} from "./typings"
import {getAppDetails, getDeviceDetails, getNetworkInformation} from "../drivers/BsSystem";
import {version} from '../../package.json'
export interface ISenderRepository {
add(userId: string, deviceId: string, event: Event, forceSend: boolean): void
flush()
}
export default class BsSender implements ISenderRepository {
private flushing: boolean = false
private flushIntervalId: number = 0
private bsNetwork
private debug
private options
private endpointURL
private userId: string = ""
private deviceId: string = ""
private queue: QueueItem[]
public constructor(
bsNetwork: NetworkProxy,
options: LogIngestorOptions
) {
this.queue = []
this.bsNetwork = bsNetwork
this.options = options
this.debug = debug(this.options.debug)
// to avoid short timeout to save cpu usage
if (this.options.flushInterval < 1000) {
this.options.flushInterval = 1000
}
if (this.options.cacheEventsInLocalstorage && typeof Storage !== 'undefined') {
try {
this.queue = JSON.parse(localStorage.getItem(this.options.cacheEventsKey) || '[]') as QueueItem[]
} catch (e) {
this.debug(Debug.Error, `Error getting queue items from cache ${e.message || ''}`)
}
}
this.endpointURL = `${this.options.baseUrl}${this.options.ingestPath}`
this.updateQueueCache()
this.flushIntervalId = +setInterval(() => {
this.flush()
}, this.options.flushInterval)
}
public flush() {
const flushItems = [...this.queue]
if (flushItems.length === 0) {
this.debug(Debug.Log, 'Skip sending empty list of logs ')
return
}
const uuids: string[] = flushItems.map((item: QueueItem) => item.uuid)
const data = flushItems.map((item: QueueItem) => item.event)
this.flushing = true
this.send(data, uuids)
}
public add(userId: string, deviceId: string, event: Event, forceSend: boolean = false) {
const item: QueueItem = {
uuid: uuidv4(),
event,
time: +new Date(),
retries: 0
}
this.userId = userId
this.deviceId = deviceId
this.queue.push(item)
this.updateQueueCache()
// send immediately. In case of error, it was already enqueued
// so will be tried again in interval tick
if (forceSend) {
this.send([item.event], [item.uuid])
}
}
private removeQueueItemsExceedMaxRetries() {
this.queue = this.queue.filter((item: QueueItem) => item.retries <= this.options.flushMaxRetries)
}
private removeFromQueue(uuids: string[]) {
this.queue = this.queue.filter((item: QueueItem) => uuids.indexOf(item.uuid) === -1)
this.updateQueueCache()
}
private updateQueueCache() {
if (this.options.cacheEventsInLocalstorage && typeof Storage !== 'undefined') {
localStorage.setItem(this.options.cacheEventsKey, JSON.stringify(this.queue))
}
}
private addRetries(uuids: string[]) {
const time = +(new Date())
this.queue
.filter((item: QueueItem) => uuids.indexOf(item.uuid) !== -1)
.forEach((item: QueueItem) => {
item.retries = (+item.retries) + 1
item.lastRetry = time
})
this.updateQueueCache()
}
private send(dataList: Event[], uuids: string[]) {
const {downlink, uplink} = getNetworkInformation()
const deviceObject: DeviceInfoObject = {
id: this.deviceId,
...getDeviceDetails()
}
const appObject: AppInfoObject = {
id: document.location.host || "NO_URL_FOUND",
min_sdk_version: 0,
target_sdk_version: 0,
...getAppDetails()
}
const eventMainObject: EventMainObject = {
s_id: this.generateSessionID(this.userId),
u_id: this.userId,
app_info: appObject,
d_info: deviceObject,
sdk: `jsBshXCF/${version}`,
up: uplink,
dn: downlink,
data: dataList
}
return this.bsNetwork.send(this.endpointURL, eventMainObject)
.then(() => {
this.debug(Debug.Info, `Flushed ${dataList.length} events`)
this.removeFromQueue(uuids)
})
.catch((e) => {
this.addRetries(uuids)
this.debug(Debug.Error, e.message || '')
})
.finally(() => {
this.flushing = false
this.removeQueueItemsExceedMaxRetries()
})
}
private generateSessionID(userId: string) {
// return `${userId}_${startTimeInMillis}_${endTimeInMillis}`;
return `${userId}_0_0`;
}
}