@benshi.ai/js-sdk
Version:
Benshi SDK
267 lines (215 loc) • 9.12 kB
text/typescript
import { createCheckers } from "ts-interface-checker"
import { EventEmitter } from 'events';
import ECommerce from "../ECommerce";
import Navigation from '../Navigation';
import { getDeviceId } from '../../drivers/BsSystem';
import ECommercePropertiesTI from "../ECommerce/typings-ti"
import BloodPropertiesTI from '../ECommerce/blood.typings-ti'
import OxygenPropertiesTI from '../ECommerce/oxygen.typings-ti'
import NavigationPropertiesTI from '../Navigation/typings-ti'
import CommonTypesTI from '../../core/commonTypes-ti'
import BsSender from "../../core/BsSender";
import BsCore from '../../core/BsCore';
import BsAppStateMonitor from "../../core/BsAppStateMonitor";
import NudgeManager from '../../core/BsNudgeManager';
import CurrencyRepository, { ICurrencyRepository } from "../../core/repositories/currency/CurrencyRepository"
import CatalogRepository, { ICatalogRepository } from "../../core/repositories/catalog/CatalogRepository";
import { CTA, INudgesRepository } from "../../core/repositories/nudges/typings";
import NudgeRepositoryFactory from "../../core/repositories/nudges/NudgeFactory";
import { BsLogOptions, LogIngestorOptions } from '../../core/typings';
import ImpressionsDetector from "../../core/impressions"
import {
ContentBlock,
IdentifyAction,
NudgeAction,
NudgeResponseType,
UserProperties
} from '../Navigation/typings';
import { offsetToISO8601 } from "../../utils";
import NotificationFactory from "../../drivers/notifications/NotificationFactory";
import { NotificationDispatcher } from "../../drivers/notifications/typings";
import BsLocalStorage from "../../drivers/storage/BsLocalStorage";
import BsNetworkQueue from "../../core/BsNetworkQueue";
import Payments from "../Payments";
import BsNetworkNodeFetch from "../../drivers/network/BsNetworkNodeFetch";
import NetworkFactory from "../../drivers/network/BsNetworkFactory";
import { NudgeManagerAction } from "../../core/BsNudgeManager.typings";
import { BsLogEvents } from "../../core/commonTypes";
export default class BsLog extends EventEmitter {
private deviceId
private sender
private bsCore
private bsNetwork
private storageDriver
private networkQueue
private nudgeManager
private notificationsEngine: NotificationDispatcher
private impressionManager
private nudgesRepository: INudgesRepository
private currencyRepository: ICurrencyRepository
private catalogRepository: ICatalogRepository
private DEFAULT_BASE_URL = 'https://api.causalfoundry.ai/v1'
private DEFAULT_INGEST_PATH = '/ingest/log'
private DEFAULT_CURRENCY_PATH = '/ingest/currency-exchange'
private DEFAULT_NUDGE_FETCH_PATH = '/nudge/sdk'
private DEFAULT_CATALOG_PATH = '/ingest/catalog'
private DEFAULT_FLUSH_INTERVAL = 10000
private DEFAULT_FLUSH_MAX_RETRIES = 10
private DEFAULT_CACHE_EVENTS_KEY = 'bslog'
private options: BsLogOptions = {
baseUrl: this.DEFAULT_BASE_URL,
currencyPath: this.DEFAULT_CURRENCY_PATH,
nudgeFetchPath: this.DEFAULT_NUDGE_FETCH_PATH,
catalogPath: this.DEFAULT_CATALOG_PATH,
activateNudgeMechanism: true,
defaultBlock: ContentBlock.Core,
debug: true
}
public constructor(key: string, options?: Partial<BsLogOptions>) {
super()
this.deviceId = getDeviceId()
if (options) {
this.options = Object.assign(this.options, options)
}
this.bsNetwork = NetworkFactory.getNetworkDriver(key)
this.networkQueue = new BsNetworkQueue(this.bsNetwork)
this.storageDriver = new BsLocalStorage()
const logIngestorDefaultOptions: LogIngestorOptions = {
flushInterval: this.DEFAULT_FLUSH_INTERVAL,
flushMaxRetries: this.DEFAULT_FLUSH_MAX_RETRIES,
ingestPath: this.DEFAULT_INGEST_PATH,
cacheEventsInLocalstorage: true,
cacheEventsKey: this.DEFAULT_CACHE_EVENTS_KEY,
}
const logIngestorOptions = {...logIngestorDefaultOptions, baseUrl: this.options.baseUrl || this.DEFAULT_BASE_URL}
this.sender = new BsSender(this.bsNetwork, logIngestorOptions)
this.bsCore = BsCore.createInstance(
this.sender,
ImpressionsDetector,
this.options.debug,
this.options.defaultBlock,
this.deviceId)
this.nudgesRepository = NudgeRepositoryFactory.getNudgeRepository(
`${this.options.baseUrl}${this.options.nudgeFetchPath}`,
this.bsNetwork,
this.storageDriver
)
this.currencyRepository = new CurrencyRepository(
`${this.options.baseUrl}${this.options.currencyPath}`,
this.bsNetwork
)
this.catalogRepository = new CatalogRepository(
`${this.options.baseUrl}${this.options.catalogPath}`,
this.networkQueue,
{}
)
ECommerce.init(this.currencyRepository, this.catalogRepository)
Navigation.init(this.catalogRepository)
Payments.init(this.currencyRepository)
BsAppStateMonitor(this)
}
/**
* @ignore
*/
private runNudgeManager(userId: string) {
if (!!this.nudgeManager) {
return
}
if (!this.options.activateNudgeMechanism) {
return
}
this.notificationsEngine = NotificationFactory.getNotificationEngine()
this.nudgeManager = new NudgeManager(
userId,
this.notificationsEngine,
this.nudgesRepository
)
this.nudgeManager.on(NudgeManagerAction.Action, (callToActionData: CTA) => {
this.emit(BsLogEvents.NudgeAction, callToActionData.type, callToActionData.cta_resource)
})
this.nudgeManager.on(NudgeManagerAction.Open, (id, nudgeInfo) => {
Navigation.logPushNotificationEvent({
nudge_id: id,
type: NudgeResponseType.InApp,
response: {
action: NudgeAction.Open
},
resolved_action: nudgeInfo
})
})
this.nudgeManager.on(NudgeManagerAction.Discard, (id, nudgeInfo) => {
Navigation.logPushNotificationEvent({
nudge_id: id,
type: NudgeResponseType.InApp,
response: {
action: NudgeAction.Discard
},
resolved_action: nudgeInfo
})
})
this.nudgeManager.on(NudgeManagerAction.Block, (id, nudgeInfo) => {
Navigation.logPushNotificationEvent({
nudge_id: id,
type: NudgeResponseType.InApp,
response: {
action: NudgeAction.Block
},
resolved_action: nudgeInfo
})
})
}
public flush() {
this.sender.flush()
}
/**
* Call this function whenever the application has information about a session event
* (Register, Login, Logout)
*
* @param {IdentifyAction} action
* @param {string} userId
* @param {UserProperties} userProps
*/
public identify(action: IdentifyAction, userId: string, userProps: UserProperties) {
const { UserInfo: IdentifyPropertiesChecker } = createCheckers(
NavigationPropertiesTI,
BloodPropertiesTI,
ECommercePropertiesTI,
OxygenPropertiesTI,
CommonTypesTI)
const userInfo = {
action,
user_props: userProps || {}
}
IdentifyPropertiesChecker.check(userInfo)
if ((action === IdentifyAction.Login || action === IdentifyAction.Register) && (userProps.country)) {
this.bsCore.login(userId)
this.runNudgeManager(userId)
const timezoneOffset = (new Date()).getTimezoneOffset()
userProps.timezone = offsetToISO8601(timezoneOffset)
this.catalogRepository.injectUser(userId, userProps)
}else{
this.bsCore.login(userId)
this.runNudgeManager(userId)
}
if (action === IdentifyAction.Logout) {
this.bsCore.logout(userId)
}
Navigation.logIdentifyEvent({action})
}
/**
* When this SDK is embedded into a web application, which has been already logged
* in another section, there is no need to call `identify` again, because the session
* is already started. Use this function to let the SDK what is the userId so it is able
* to include in the subsequent events.
*
* An example of an scenario where this is useful is: a native application where
* some section is implemented in HTML/JS with a WebView. In this case, the session
* has already started in the native part, so do not call `identify` within the JS section,
* just call `setUserId`
*
* @param userId the string that represents unambiguously this user
*/
public setUserId(userId: string) {
this.bsCore.login(userId)
}
}