launchdarkly-react-native-client-sdk
Version:
LaunchDarkly Client-side SDK for React Native
297 lines (250 loc) • 10.4 kB
JavaScript
import { NativeModules, NativeEventEmitter } from 'react-native';
import { version } from './package.json';
import { validateContext } from './src/contextUtils';
import { LDInvalidUserError } from 'launchdarkly-js-sdk-common/src/errors';
import { invalidContext } from 'launchdarkly-js-sdk-common/src/messages';
let LaunchdarklyReactNativeClient = NativeModules.LaunchdarklyReactNativeClient;
export default class LDClient {
constructor() {
this.eventEmitter = new NativeEventEmitter(LaunchdarklyReactNativeClient);
this.flagListeners = {};
this.allFlagsListeners = {};
this.connectionModeListeners = {};
this.eventEmitter.addListener(LaunchdarklyReactNativeClient.FLAG_PREFIX, (body) => this._flagUpdateListener(body));
this.eventEmitter.addListener(LaunchdarklyReactNativeClient.ALL_FLAGS_PREFIX, (body) =>
this._allFlagsUpdateListener(body),
);
this.eventEmitter.addListener(LaunchdarklyReactNativeClient.CONNECTION_MODE_PREFIX, (body) =>
this._connectionModeUpdateListener(body),
);
}
getVersion() {
return String(version);
}
configure(config, context, timeout) {
if (validateContext(context)) {
return LaunchdarklyReactNativeClient.isInitialized('default').then(
(ignored) => {
throw new Error('LaunchDarkly SDK already initialized');
},
() => {
const configWithOverriddenDefaults = Object.assign(
{
enableAutoEnvAttributes: false,
backgroundPollingInterval: 3600000, // the iOS SDK defaults this to 900000
disableBackgroundUpdating: false, // the iOS SDK defaults this to true
wrapperName: 'react-native-client-sdk',
wrapperVersion: this.getVersion(),
},
config,
);
if (timeout == undefined) {
return LaunchdarklyReactNativeClient.configure(configWithOverriddenDefaults, context);
} else {
return LaunchdarklyReactNativeClient.configureWithTimeout(configWithOverriddenDefaults, context, timeout);
}
},
);
} else {
return Promise.reject(new LDInvalidUserError(invalidContext(), null));
}
}
_validateDefault(defaultType, defaultValue, validator) {
if (typeof defaultValue !== defaultType || (typeof validator === 'function' && !validator(defaultValue))) {
return Promise.reject(new Error('Missing or invalid defaultValue for variation call'));
}
return Promise.resolve();
}
_normalizeEnv(environment) {
if (typeof environment !== 'string') {
return 'default';
}
return environment;
}
boolVariation(flagKey, defaultValue, environment) {
return this._validateDefault('boolean', defaultValue).then(() =>
LaunchdarklyReactNativeClient.boolVariation(flagKey, defaultValue, this._normalizeEnv(environment)),
);
}
numberVariation(flagKey, defaultValue, environment) {
return this._validateDefault('number', defaultValue, (val) => !isNaN(val)).then(() =>
LaunchdarklyReactNativeClient.numberVariation(flagKey, defaultValue, this._normalizeEnv(environment)),
);
}
stringVariation(flagKey, defaultValue, environment) {
if (defaultValue != null && typeof defaultValue !== 'string') {
return Promise.reject(new Error('Missing or invalid defaultValue for variation call'));
} else if (defaultValue === undefined) {
defaultValue = null;
}
return LaunchdarklyReactNativeClient.stringVariation(flagKey, defaultValue, this._normalizeEnv(environment));
}
jsonVariation(flagKey, defaultValue, environment) {
if (defaultValue === undefined) {
defaultValue = null;
}
return LaunchdarklyReactNativeClient.jsonVariation(flagKey, defaultValue, this._normalizeEnv(environment));
}
boolVariationDetail(flagKey, defaultValue, environment) {
return this._validateDefault('boolean', defaultValue).then(() =>
LaunchdarklyReactNativeClient.boolVariationDetail(flagKey, defaultValue, this._normalizeEnv(environment)),
);
}
numberVariationDetail(flagKey, defaultValue, environment) {
return this._validateDefault('number', defaultValue, (val) => !isNaN(val)).then(() =>
LaunchdarklyReactNativeClient.numberVariationDetail(flagKey, defaultValue, this._normalizeEnv(environment)),
);
}
stringVariationDetail(flagKey, defaultValue, environment) {
if (defaultValue != null && typeof defaultValue !== 'string') {
return Promise.reject(new Error('Missing or invalid defaultValue for variation call'));
} else if (defaultValue === undefined) {
defaultValue = null;
}
return LaunchdarklyReactNativeClient.stringVariationDetail(flagKey, defaultValue, this._normalizeEnv(environment));
}
jsonVariationDetail(flagKey, defaultValue, environment) {
if (defaultValue === undefined) {
defaultValue = null;
}
return LaunchdarklyReactNativeClient.jsonVariationDetail(flagKey, defaultValue, this._normalizeEnv(environment));
}
allFlags(environment) {
return LaunchdarklyReactNativeClient.allFlags(this._normalizeEnv(environment));
}
track(eventName, data, metricValue, environment) {
if (data === undefined) {
data = null;
}
if (typeof metricValue === 'number') {
LaunchdarklyReactNativeClient.trackMetricValue(eventName, data, metricValue, this._normalizeEnv(environment));
} else {
LaunchdarklyReactNativeClient.trackData(eventName, data, this._normalizeEnv(environment));
}
}
setOffline() {
return LaunchdarklyReactNativeClient.setOffline();
}
isOffline() {
return LaunchdarklyReactNativeClient.isOffline();
}
setOnline() {
return LaunchdarklyReactNativeClient.setOnline();
}
isInitialized(environment) {
return LaunchdarklyReactNativeClient.isInitialized(this._normalizeEnv(environment));
}
flush() {
LaunchdarklyReactNativeClient.flush();
}
close() {
LaunchdarklyReactNativeClient.close();
}
identify(context) {
if (validateContext(context)) {
return LaunchdarklyReactNativeClient.identify(context);
} else {
return Promise.reject(new LDInvalidUserError(invalidContext(), null));
}
}
_flagUpdateListener(changedFlag) {
const flagKey = changedFlag.flagKey;
const listenerId = changedFlag.listenerId;
if (this.flagListeners.hasOwnProperty(listenerId)) {
let listeners = this.flagListeners[listenerId];
for (const listener of listeners) {
listener(flagKey);
}
}
}
_allFlagsUpdateListener(changedFlags) {
const flagKeys = changedFlags.flagKeys;
const listenerId = changedFlags.listenerId;
if (this.allFlagsListeners.hasOwnProperty(listenerId)) {
this.allFlagsListeners[listenerId](flagKeys);
}
}
_connectionModeUpdateListener(connectionStatus) {
const connectionMode = connectionStatus.connectionMode;
const listenerId = connectionStatus.listenerId;
if (this.connectionModeListeners.hasOwnProperty(listenerId)) {
this.connectionModeListeners[listenerId](connectionMode);
}
}
_envConcat(env, flagKey) {
return env.concat(';', flagKey);
}
registerFeatureFlagListener(flagKey, callback, environment) {
if (typeof callback !== 'function') {
return;
}
const env = this._normalizeEnv(environment);
const multiFlagKey = this._envConcat(env, flagKey);
if (this.flagListeners.hasOwnProperty(multiFlagKey)) {
this.flagListeners[multiFlagKey].push(callback);
} else {
this.flagListeners[multiFlagKey] = [callback];
LaunchdarklyReactNativeClient.registerFeatureFlagListener(flagKey, env);
}
}
unregisterFeatureFlagListener(flagKey, callback, environment) {
const env = this._normalizeEnv(environment);
const multiFlagKey = this._envConcat(env, flagKey);
if (!this.flagListeners.hasOwnProperty(multiFlagKey)) {
return;
}
this.flagListeners[multiFlagKey] = this.flagListeners[multiFlagKey].filter((listener) => listener != callback);
if (this.flagListeners[multiFlagKey].length == 0) {
LaunchdarklyReactNativeClient.unregisterFeatureFlagListener(flagKey, env);
delete this.flagListeners[multiFlagKey];
}
}
registerCurrentConnectionModeListener(listenerId, callback, environment) {
if (typeof callback !== 'function') {
return;
}
const env = this._normalizeEnv(environment);
const multiListenerId = this._envConcat(env, listenerId);
this.connectionModeListeners[multiListenerId] = callback;
LaunchdarklyReactNativeClient.registerCurrentConnectionModeListener(listenerId, env);
}
unregisterCurrentConnectionModeListener(listenerId, environment) {
const env = this._normalizeEnv(environment);
const multiListenerId = this._envConcat(env, listenerId);
if (!this.connectionModeListeners.hasOwnProperty(multiListenerId)) {
return;
}
LaunchdarklyReactNativeClient.unregisterCurrentConnectionModeListener(listenerId, env);
delete this.connectionModeListeners[multiListenerId];
}
registerAllFlagsListener(listenerId, callback, environment) {
if (typeof callback !== 'function') {
return;
}
const env = this._normalizeEnv(environment);
const multiListenerId = this._envConcat(env, listenerId);
this.allFlagsListeners[multiListenerId] = callback;
LaunchdarklyReactNativeClient.registerAllFlagsListener(listenerId, env);
}
unregisterAllFlagsListener(listenerId, environment) {
const env = this._normalizeEnv(environment);
const multiListenerId = this._envConcat(env, listenerId);
if (!this.allFlagsListeners.hasOwnProperty(multiListenerId)) {
return;
}
LaunchdarklyReactNativeClient.unregisterAllFlagsListener(listenerId, env);
delete this.allFlagsListeners[multiListenerId];
}
getConnectionMode(environment) {
return LaunchdarklyReactNativeClient.getConnectionMode(this._normalizeEnv(environment));
}
getLastSuccessfulConnection(environment) {
return LaunchdarklyReactNativeClient.getLastSuccessfulConnection(this._normalizeEnv(environment));
}
getLastFailedConnection(environment) {
return LaunchdarklyReactNativeClient.getLastFailedConnection(this._normalizeEnv(environment));
}
getLastFailure(environment) {
return LaunchdarklyReactNativeClient.getLastFailure(this._normalizeEnv(environment));
}
}