@rudderstack/rudder-sdk-react-native
Version:
Rudder React Native SDK
398 lines (368 loc) • 12.2 kB
text/typescript
import { Platform } from 'react-native';
import AsyncLock from 'async-lock';
import { configure } from './RudderConfiguration';
import bridge, { Configuration } from './NativeRudderBridge';
import { logInit, logDebug, logError, logWarn } from './Logger';
import { SDK_VERSION, ENABLE_GZIP } from './Constants';
import IRudderContext from './IRudderContext';
import { filterNaN } from './FilterNaN';
const lock = new AsyncLock();
function validateConfiguration(configuration: Configuration) {
if (configuration.controlPlaneUrl && typeof configuration.controlPlaneUrl != 'string') {
logWarn("setup : 'controlPlaneUrl' must be a string. Falling back to the default value");
delete configuration.controlPlaneUrl;
}
if (configuration.flushQueueSize && !Number.isInteger(configuration.flushQueueSize)) {
logWarn("setup : 'flushQueueSize' must be an integer. Falling back to the default value");
delete configuration.flushQueueSize;
}
if (configuration.dbCountThreshold && !Number.isInteger(configuration.dbCountThreshold)) {
logWarn("setup : 'dbCountThreshold' must be an integer. Falling back to the default value");
delete configuration.dbCountThreshold;
}
if (configuration.sleepTimeOut && !Number.isInteger(configuration.sleepTimeOut)) {
logWarn("setup : 'sleepTimeOut' must be an integer. Falling back to the default value");
delete configuration.sleepTimeOut;
}
if (configuration.logLevel && !Number.isInteger(configuration.logLevel)) {
logWarn(
"setup : 'logLevel' must be an integer. Use RUDDER_LOG_LEVEL to set this value.Falling back to the default value",
);
delete configuration.logLevel;
}
if (
configuration.configRefreshInterval &&
!Number.isInteger(configuration.configRefreshInterval)
) {
logWarn(
"setup : 'configRefreshInterval' must be an integer. Falling back to the default value",
);
delete configuration.configRefreshInterval;
}
if (
configuration.trackAppLifecycleEvents &&
typeof configuration.trackAppLifecycleEvents != 'boolean'
) {
logWarn(
"setup : 'trackAppLifecycleEvents' must be a boolen. Falling back to the default value",
);
delete configuration.trackAppLifecycleEvents;
}
if (configuration.recordScreenViews && typeof configuration.recordScreenViews != 'boolean') {
logWarn("setup : 'recordScreenViews' must be a boolen. Falling back to the default value");
delete configuration.recordScreenViews;
}
if (configuration.autoCollectAdvertId && typeof configuration.autoCollectAdvertId != 'boolean') {
logWarn("setup : 'autoCollectAdvertId' must be a boolen. Falling back to the default value");
delete configuration.autoCollectAdvertId;
}
if (configuration.autoSessionTracking && typeof configuration.autoSessionTracking != 'boolean') {
logWarn("setup : 'autoSessionTracking' must be a boolen. Falling back to the default value");
delete configuration.autoSessionTracking;
}
if (configuration.sessionTimeout && !Number.isInteger(configuration.sessionTimeout)) {
logWarn("setup : 'sessionTimeout' must be an integer. Falling back to the default value");
delete configuration.sessionTimeout;
}
if (
configuration.enableBackgroundMode &&
typeof configuration.enableBackgroundMode != 'boolean'
) {
logWarn("setup : 'enableBackgroundMode' must be a boolen. Falling back to the default value");
delete configuration.enableBackgroundMode;
}
if (configuration.collectDeviceId && typeof configuration.collectDeviceId != 'boolean') {
logWarn("setup : 'collectDeviceId' must be a boolean. Falling back to the default value");
delete configuration.collectDeviceId;
}
if (
typeof configuration.enableGzip !== 'undefined' &&
typeof configuration.enableGzip != 'boolean'
) {
logWarn(
`setup : 'enableGzip' must be a boolean. Falling back to the default value ${ENABLE_GZIP}`,
);
delete configuration.enableGzip;
}
}
// setup the RudderSDK with writeKey and Config.
async function setup(
writeKey: string,
configuration: Configuration = {},
options: Record<string, unknown> | null = null,
) {
if (writeKey == undefined || typeof writeKey != 'string' || writeKey == '') {
logError('setup: writeKey is incorrect. Aborting');
return;
}
if (
!configuration.dataPlaneUrl ||
typeof configuration.dataPlaneUrl != 'string' ||
configuration.dataPlaneUrl! == ''
) {
logError('setup: dataPlaneUrl is incorrect. Aborting');
return;
}
// init log level
if (configuration.logLevel && Number.isInteger(configuration.logLevel)) {
logInit(configuration.logLevel);
}
logDebug(`Initializing Rudder RN SDK version: ${SDK_VERSION}`);
validateConfiguration(configuration);
// Acquire a lock before calling the setup of Native Modules
await lock.acquire('lock', async function (done) {
const config = await configure(writeKey, configuration);
logDebug('setup: created config');
await bridge.setup(config as Record<string, unknown>, options);
logDebug('setup: setup completed');
done();
});
}
// wrapper for `track` method
async function track(
event: string,
properties: Record<string, unknown> | null = null,
options: Record<string, unknown> | null = null,
) {
if (event == undefined) {
logWarn("track: Mandatory field 'event' missing");
return;
}
if (typeof event != 'string') {
logWarn("track: 'event' must be a string");
return;
}
bridge.track(event, filterNaN(properties), filterNaN(options));
}
// wrapper for `screen` method
async function screen(
name: string,
properties: Record<string, unknown> | null = null,
options: Record<string, unknown> | null = null,
) {
if (name == undefined) {
logWarn("screen: Mandatory field 'name' missing");
return;
}
if (typeof name != 'string') {
logWarn("screen: 'name' must be a string");
return;
}
bridge.screen(name, filterNaN(properties), filterNaN(options));
}
// wrapper for `identify` method
async function identify(
userId: string,
traits: Record<string, unknown>,
options: Record<string, unknown>,
): Promise<void>;
async function identify(
traits: Record<string, unknown>,
options: Record<string, unknown>,
): Promise<void>;
async function identify(
userIdOrTraits: string | Record<string, unknown>,
traitsOrOptions: Record<string, unknown> | null = null,
options: Record<string, unknown> | null = null,
) {
if (userIdOrTraits == undefined) {
logWarn('identify: atleast one of userId or traits is required');
return;
}
let _userId;
let _traits;
let _options;
if (typeof userIdOrTraits == 'string') {
// userIdOrTraits contains userId
_userId = userIdOrTraits;
_traits = traitsOrOptions;
_options = options;
} else if (typeof userIdOrTraits == 'object') {
// userIdOrTraits contains traits
_userId = '';
_traits = userIdOrTraits;
_options = traitsOrOptions;
} else {
logWarn('identify : Unsupported argument type passed to identify');
return;
}
bridge.identify(_userId, filterNaN(_traits), filterNaN(_options));
}
// wrapper for `group` method
async function group(
groupId: string,
traits: Record<string, unknown> | null = null,
options: Record<string, unknown> | null = null,
) {
if (groupId == undefined) {
logWarn("group: Mandatory field 'groupId' missing");
return;
}
if (typeof groupId != 'string') {
logWarn("group: 'groupId' must be a string");
return;
}
bridge.group(groupId, filterNaN(traits), filterNaN(options));
}
// wrapper for `alias` method
async function alias(newId: string, options?: Record<string, unknown> | null): Promise<void>;
async function alias(
newId: string,
previousId: string,
options?: Record<string, unknown> | null,
): Promise<void>;
async function alias(
newId: string,
previousIdOrOptions: string | Record<string, unknown> | null = null,
options: Record<string, unknown> | undefined | null = null,
): Promise<void> {
// Validate newId
if (!newId) {
logWarn("alias: Mandatory field 'newId' is missing");
return Promise.resolve();
}
if (typeof newId !== 'string') {
logWarn("alias: 'newId' must be a string");
return Promise.resolve();
}
// Handle cases based on the type of previousIdOrOptions
if (typeof previousIdOrOptions === 'string') {
return bridge.alias(newId, previousIdOrOptions, filterNaN(options));
}
if (
previousIdOrOptions &&
typeof previousIdOrOptions === 'object' &&
!Array.isArray(previousIdOrOptions)
) {
return bridge.alias(newId, null, filterNaN(previousIdOrOptions));
}
return bridge.alias(newId, null, filterNaN(options));
}
async function putDeviceToken(token: string): Promise<void>;
/**
* @deprecated use putDeviceToken{@link putDeviceToken(token: string)} instead
*/
async function putDeviceToken(androidToken: string, iOSToken: string): Promise<void>;
async function putDeviceToken(token: string, iOSToken: string | null = null): Promise<void> {
if (Platform.OS == 'ios' && iOSToken) {
bridge.putDeviceToken(iOSToken);
} else if (token) {
bridge.putDeviceToken(token);
}
}
/**
* @deprecated use putAdvertisingId{@link putAdvertisingId(advertisingId: string)} instead
*/
async function setAdvertisingId(androidId: string, iOSId: string) {
switch (Platform.OS) {
case 'ios':
if (iOSId) {
putAdvertisingId(iOSId);
}
break;
case 'android':
if (androidId) {
putAdvertisingId(androidId);
}
break;
}
}
async function putAdvertisingId(advertisingId: string) {
if (advertisingId) {
bridge.putAdvertisingId(advertisingId);
}
}
async function clearAdvertisingId() {
bridge.clearAdvertisingId();
}
/**
* @deprecated use putAnonymousId{@link putAnonymousId(anonymousId: string)} instead
*/
async function setAnonymousId(anonymousId: string) {
if (anonymousId) {
putAnonymousId(anonymousId);
}
}
async function putAnonymousId(anonymousId: string) {
if (anonymousId) {
bridge.putAnonymousId(anonymousId);
}
}
async function reset(clearAnonymousId = false) {
const clearAnonymousIdVal = clearAnonymousId === true;
if (typeof clearAnonymousId !== 'boolean') {
logWarn("reset: 'clearAnonymousId' must be a boolean");
}
bridge.reset(clearAnonymousIdVal);
}
async function flush() {
bridge.flush();
}
async function optOut(optOut: boolean) {
const optOutVal = optOut === true;
if (typeof optOut !== 'boolean') {
logWarn("optOut: 'optOut' must be a boolean");
}
bridge.optOut(optOutVal);
}
// eslint-disable-next-line @typescript-eslint/ban-types
async function registerCallback(name: string, callback: (data: unknown) => void) {
if (name) {
bridge.registerCallback(name, callback);
}
}
async function getRudderContext(): Promise<IRudderContext | null> {
const context: IRudderContext | null = await bridge.getRudderContext();
return context ?? null;
}
async function startSession(sessionId?: number): Promise<void> {
if (sessionId === undefined) {
bridge.startSession('');
} else if (!Number.isInteger(sessionId)) {
logWarn("startSession: 'sessionId' must be an integer");
} else {
if (sessionId.toString().length < 10) {
logWarn("startSession: 'sessionId' length should be at least 10, hence ignoring it");
return;
}
bridge.startSession(sessionId.toString());
}
}
async function endSession() {
bridge.endSession();
}
async function getSessionId(): Promise<number | null> {
try {
const sessionId: number | null = await bridge.getSessionId();
if (sessionId === null || sessionId === undefined) {
return null;
}
return Number(sessionId);
} catch (e) {
logError('getSessionId: Failed to get sessionId: ' + e);
return null;
}
}
const rudderClient = {
setup,
track,
screen,
identify,
group,
alias,
reset,
flush,
optOut,
putDeviceToken,
putAdvertisingId,
setAdvertisingId,
clearAdvertisingId,
putAnonymousId,
setAnonymousId,
registerCallback,
getRudderContext,
startSession,
endSession,
getSessionId,
};
export default rudderClient;