UNPKG

@causalfoundry/js-sdk

Version:

Causal Foundry WEB SDK (JS/TS)

353 lines (300 loc) 9.6 kB
import { EventEmitter } from "events"; import Navigation from "../Navigation"; import { clearLocalStorageUserId, getDeviceId, getLocalStorageUserId, setLocalStorageUserId, } from "../../drivers/CfSystem"; import CfSender from "../../core/CfSender"; import CfCore from "../../core/CfCore"; import CfAppStateMonitor from "../../core/CfAppStateMonitor"; import NudgeManager from "../../core/CfNudgeManager"; import { CTA, INudgesRepository, NudgeCallBack, } from "../../core/repositories/nudges/typings"; import NudgeRepositoryFactory from "../../core/repositories/nudges/NudgeFactory"; import { CfLogOptions, LogIngestorOptions } from "../../core/typings"; import ImpressionsDetector from "../../core/impressions"; import { ContentBlock, IdentityAction, NavigationTypes, SiteCatalog, UserCatalog, } from "../Navigation/typings"; import NotificationFactory from "../../drivers/notifications/NotificationFactory"; import { NotificationDispatcher } from "../../drivers/notifications/typings"; import CfLocalStorage from "../../drivers/storage/CfLocalStorage"; import CfNetworkQueue from "../../core/CfNetworkQueue"; import NetworkFactory from "../../drivers/network/CfNetworkFactory"; import { NudgeManagerAction } from "../../core/CfNudgeManager.typings"; import { CfLogEvents, NudgeScreenType } from "../../core/commonTypes"; import CfExceptionHandler from "../../core/CfExceptionHandler"; import isBrowser from "is-in-browser"; import EmergencyMgmt from "../EmergencyMgmt"; import { validateAndSaveSiteCatalog, validateAndSaveUserCatalog, } from "../Navigation/CoreCatalogValidator"; export default class CfLog extends EventEmitter { private deviceId; private sender; private cfCore; private cfNetwork; private storageDriver; private networkQueue; private nudgeManager; private notificationsEngine: NotificationDispatcher; private nudgesRepository: INudgesRepository; private DEFAULT_BASE_URL = "https://api.kenkai.io/v1"; private DEFAULT_INGEST_PATH = "/ingest/events"; private DEFAULT_DIMENSION_PATH = "/ingest/dimensions"; private DEFAULT_NUDGE_FETCH_PATH = "/action/sdk/pull"; private DEFAULT_CATALOG_PATH = "/ingest/dimensions"; private DEFAULT_CDN_URL = "https://cdn.causalfoundry.ai/"; private DEFAULT_FLUSH_INTERVAL = 10000; private DEFAULT_FLUSH_MAX_RETRIES = 2; private DEFAULT_CACHE_EVENTS_KEY = "cfEvents"; private DEFAULT_CACHE_CATALOG_KEY = "cfCatalog"; private options: CfLogOptions = { cdnUrl: this.DEFAULT_CDN_URL, baseUrl: this.DEFAULT_BASE_URL, allowAnonymousUsers: false, nudgeFetchPath: this.DEFAULT_NUDGE_FETCH_PATH, catalogPath: this.DEFAULT_CATALOG_PATH, activateNudgeMechanism: true, selfManagedNudges: false, defaultBlock: ContentBlock.Core, debug: true, isUsingAsInternalWrapper: false, pauseSDK: false, }; public constructor(key: string, options?: Partial<CfLogOptions>) { super(); this.deviceId = getDeviceId(); if (options) { this.options = Object.assign(this.options, options); } this.cfNetwork = NetworkFactory.getNetworkDriver(key); this.networkQueue = new CfNetworkQueue(this.cfNetwork); this.storageDriver = new CfLocalStorage(); const logIngestorDefaultOptions: LogIngestorOptions = { flushInterval: this.DEFAULT_FLUSH_INTERVAL, flushMaxRetries: this.DEFAULT_FLUSH_MAX_RETRIES, ingestPath: this.DEFAULT_INGEST_PATH, dimensionPath: this.DEFAULT_DIMENSION_PATH, cacheEventsInLocalstorage: true, cacheEventsKey: this.DEFAULT_CACHE_EVENTS_KEY, cacheCatalogKey: this.DEFAULT_CACHE_CATALOG_KEY, }; const updatedIngestionOptions = { ...logIngestorDefaultOptions, ...options, }; const logIngestorOptions = { ...updatedIngestionOptions, baseUrl: this.options.baseUrl || this.DEFAULT_BASE_URL, }; this.sender = new CfSender(this.cfNetwork, logIngestorOptions); this.cfCore = CfCore.createInstance( this.sender, ImpressionsDetector, this.options.debug, this.options.defaultBlock, this.deviceId, this.options.isUsingAsInternalWrapper ); this.nudgesRepository = NudgeRepositoryFactory.getNudgeRepository( `${this.options.baseUrl}${this.options.nudgeFetchPath}`, this.cfNetwork, this.storageDriver ); CfAppStateMonitor(this); if ( this.options.allowAnonymousUsers && isBrowser && this.deviceId && getLocalStorageUserId() === "" ) { this.fetchNudgesForAnonUsers(this.deviceId); } } public flush() { if (this.options.pauseSDK) { return; } this.sender.flush(); } /** * Call this function whenever the application has information about a session event * (Register, Login, Logout) * * @param {IdentityAction} action * @param {string} userId * @param {UserCatalog} userProps * @param {string} referralCode */ public identify( action: IdentityAction, userId: string, userProps?: UserCatalog, referralCode?: string ) { if (this.options.pauseSDK) { return; } if (userId == "") { new CfExceptionHandler().throwAndLogError( NavigationTypes.Identify, "invalid-user-id" ); return; } this.cfCore.login(userId); if (action === IdentityAction.Logout) { clearLocalStorageUserId(); } else { this.runNudgeManager(userId); setLocalStorageUserId(userId); if (userProps) { validateAndSaveUserCatalog(userId, userProps); } } Navigation.logIdentifyEvent({ action: action, referral_code: referralCode, }); } public userBlockStatus(isBlocked: boolean, reason: string, remarks?: string) { if (this.options.pauseSDK) { return; } if (isBlocked == undefined) { new CfExceptionHandler().throwAndLogError( NavigationTypes.Identify, "invalid-block-status" ); } else if (reason == "") { new CfExceptionHandler().throwAndLogError( NavigationTypes.Identify, "invalid-reason" ); } const userIdentifyObject = { action: isBlocked ? IdentityAction.Blocked : IdentityAction.UnBlocked, referral_code: "", blocked: { reason: reason, remarks: remarks, }, }; Navigation.logIdentifyEvent(userIdentifyObject); } public updateUserCatalog(userId: string, userCatalog: UserCatalog) { if (this.options.pauseSDK) { return; } validateAndSaveUserCatalog(userId, userCatalog); } public updateSiteCatalog(siteId: string, siteCatalog: SiteCatalog) { if (this.options.pauseSDK) { return; } validateAndSaveSiteCatalog(siteId, siteCatalog); } /** * 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 a 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) { if (this.options.pauseSDK) { return; } if (!userId) { new CfExceptionHandler().throwAndLogError( NavigationTypes.Identify, "invalid-user-id" ); return; } this.cfCore.login(userId); } /** * @ignore */ private runNudgeManager(userId: string) { if (this.options.pauseSDK) { return; } if (this.nudgeManager && !this.options.allowAnonymousUsers) { return; } else { this.nudgeManager = null; } if (!this.options.activateNudgeMechanism) { return; } if (this.options.selfManagedNudges) { return; } this.setupNudgeManager(userId, NudgeScreenType.None, false); } private fetchNudgesForScreen( userId: string, nudgeScreenType: NudgeScreenType ) { if (this.options.pauseSDK) { return; } if (this.nudgeManager) { this.nudgeManager = null; } if (!this.options.activateNudgeMechanism) { // console.error("[cfLog-nudge]", "enable activateNudgeMechanism in CfLogOptions") return; } if (!this.options.selfManagedNudges) { // console.error("[cfLog-nudge]", "enable selfManagedNudges in CfLogOptions") return; } this.setupNudgeManager(userId, nudgeScreenType, true); } private fetchNudgesForAnonUsers(deviceId: string) { if (this.options.pauseSDK) { return; } if (this.nudgeManager) { this.nudgeManager = null; } this.setupNudgeManager(deviceId, NudgeScreenType.None, true); } private setupNudgeManager( userId: string, nudgeScreenType: NudgeScreenType, isOneTimeOnly: boolean ) { this.notificationsEngine = NotificationFactory.getNotificationEngine(); this.nudgeManager = new NudgeManager( userId, this.notificationsEngine, this.nudgesRepository, nudgeScreenType, isOneTimeOnly ); this.nudgeManager.on(NudgeManagerAction.Action, (callToActionData: CTA) => { this.emit(CfLogEvents.NudgeAction, callToActionData.type, callToActionData.cta_resource) }) } }