@causalfoundry/js-sdk
Version:
Causal Foundry WEB SDK (JS/TS)
353 lines (300 loc) • 9.6 kB
text/typescript
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)
})
}
}