Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: rename and reuse some utility functions #282

Merged
merged 4 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions src/sdkClient/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { IEvaluationResult } from '../evaluator/types';
import { SplitIO, ImpressionDTO } from '../types';
import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
import { ISdkFactoryContext } from '../sdkFactory/types';
import { isStorageSync } from '../trackers/impressionObserver/utils';
import { isConsumerMode } from '../utils/settingsValidation/mode';
import { Method } from '../sync/submitters/types';

const treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY };
Expand All @@ -28,6 +28,7 @@ function treatmentsNotReady(featureFlagNames: string[]) {
export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | SplitIO.IAsyncClient {
const { sdkReadinessManager: { readinessManager }, storage, settings, impressionsTracker, eventTracker, telemetryTracker } = params;
const { log, mode } = settings;
const isAsync = isConsumerMode(mode);

function getTreatment(key: SplitIO.SplitKey, featureFlagName: string, attributes: SplitIO.Attributes | undefined, withConfig = false, methodName = GET_TREATMENT) {
const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT);
Expand All @@ -43,9 +44,9 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl

const evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
evaluateFeature(log, key, featureFlagName, attributes, storage) :
isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
treatmentNotReady :
Promise.resolve(treatmentNotReady); // Promisify if async
isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
Promise.resolve(treatmentNotReady) :
treatmentNotReady;

return thenable(evaluation) ? evaluation.then((res) => wrapUp(res)) : wrapUp(evaluation);
}
Expand All @@ -71,9 +72,9 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl

const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
evaluateFeatures(log, key, featureFlagNames, attributes, storage) :
isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
treatmentsNotReady(featureFlagNames) :
Promise.resolve(treatmentsNotReady(featureFlagNames)); // Promisify if async
isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
Promise.resolve(treatmentsNotReady(featureFlagNames)) :
treatmentsNotReady(featureFlagNames);

return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations);
}
Expand All @@ -100,7 +101,9 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl

const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName) :
isStorageSync(settings) ? {} : Promise.resolve({}); // Promisify if async
isAsync ?
Promise.resolve({}) :
{};

return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations);
}
Expand Down
10 changes: 5 additions & 5 deletions src/sdkClient/clientInputValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { CONTROL, CONTROL_WITH_CONFIG, GET_TREATMENT, GET_TREATMENTS, GET_TREATM
import { IReadinessManager } from '../readiness/types';
import { MaybeThenable } from '../dtos/types';
import { ISettings, SplitIO } from '../types';
import { isStorageSync } from '../trackers/impressionObserver/utils';
import { isConsumerMode } from '../utils/settingsValidation/mode';
import { validateFlagSets } from '../utils/settingsValidation/splitFilters';

/**
Expand All @@ -25,8 +25,8 @@ import { validateFlagSets } from '../utils/settingsValidation/splitFilters';
*/
export function clientInputValidationDecorator<TClient extends SplitIO.IClient | SplitIO.IAsyncClient>(settings: ISettings, client: TClient, readinessManager: IReadinessManager): TClient {

const log = settings.log;
const isSync = isStorageSync(settings);
const { log, mode } = settings;
const isAsync = isConsumerMode(mode);

/**
* Avoid repeating this validations code
Expand Down Expand Up @@ -59,7 +59,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
}

function wrapResult<T>(value: T): MaybeThenable<T> {
return isSync ? value : Promise.resolve(value);
return isAsync ? Promise.resolve(value) : value;
}

function getTreatment(maybeKey: SplitIO.SplitKey, maybeFeatureFlagName: string, maybeAttributes?: SplitIO.Attributes) {
Expand Down Expand Up @@ -159,7 +159,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
if (isNotDestroyed && key && tt && event && eventValue !== false && properties !== false) { // @ts-expect-error
return client.track(key, tt, event, eventValue, properties, size);
} else {
return isSync ? false : Promise.resolve(false);
return isAsync ? Promise.resolve(false) : false;
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/sdkManager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ISplitsCacheAsync, ISplitsCacheSync } from '../storages/types';
import { ISdkReadinessManager } from '../readiness/types';
import { ISplit } from '../dtos/types';
import { ISettings, SplitIO } from '../types';
import { isStorageSync } from '../trackers/impressionObserver/utils';
import { isConsumerMode } from '../utils/settingsValidation/mode';
import { SPLIT_FN_LABEL, SPLITS_FN_LABEL, NAMES_FN_LABEL } from '../utils/constants';

function collectTreatments(splitObject: ISplit) {
Expand Down Expand Up @@ -51,8 +51,8 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
{ readinessManager, sdkStatus }: ISdkReadinessManager,
): TSplitCache extends ISplitsCacheAsync ? SplitIO.IAsyncManager : SplitIO.IManager {

const log = settings.log;
const isSync = isStorageSync(settings);
const { log, mode } = settings;
const isAsync = isConsumerMode(mode);

return objectAssign(
// Proto-linkage of the readiness Event Emitter
Expand All @@ -64,7 +64,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
split(featureFlagName: string) {
const splitName = validateSplit(log, featureFlagName, SPLIT_FN_LABEL);
if (!validateIfNotDestroyed(log, readinessManager, SPLIT_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
return isSync ? null : Promise.resolve(null);
return isAsync ? Promise.resolve(null) : null;
}

const split = splits.getSplit(splitName);
Expand All @@ -85,7 +85,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
*/
splits() {
if (!validateIfNotDestroyed(log, readinessManager, SPLITS_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLITS_FN_LABEL)) {
return isSync ? [] : Promise.resolve([]);
return isAsync ? Promise.resolve([]) : [];
}
const currentSplits = splits.getAll();

Expand All @@ -98,7 +98,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
*/
names() {
if (!validateIfNotDestroyed(log, readinessManager, NAMES_FN_LABEL) || !validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
return isSync ? [] : Promise.resolve([]);
return isAsync ? Promise.resolve([]) : [];
}
const splitNames = splits.getSplitNames();

Expand Down
2 changes: 1 addition & 1 deletion src/storages/inLocalStorage/SplitsCacheInLocal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
}

private _checkFilterQuery() {
const { queryString } = this.splitFiltersValidation;
const queryString = this.splitFiltersValidation.queryString;
const queryKey = this.keys.buildSplitsFilterQueryKey();
const currentQueryString = localStorage.getItem(queryKey);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @ts-nocheck
import { ImpressionCountsCacheInRedis } from '../ImpressionCountsCacheInRedis';
import { truncateTimeFrame } from '../../../utils/time';
import { RedisMock } from '../../../utils/redis/RedisMock';
import { RedisAdapterMock } from './RedisAdapter.mock';
import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock';
import { RedisAdapter } from '../RedisAdapter';

Expand Down Expand Up @@ -109,7 +109,7 @@ describe('IMPRESSION COUNTS CACHE IN REDIS', () => {

test('start and stop task', (done) => {

const connection = new RedisMock();
const connection = new RedisAdapterMock();
const refreshRate = 100;
const counter = new ImpressionCountsCacheInRedis(loggerMock, key, connection, undefined, refreshRate);
counter.track('feature1', timestamp, 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ const IDENTITY_METHODS: string[] = [];
const ASYNC_METHODS = ['rpush', 'hincrby'];
const PIPELINE_METHODS = ['rpush', 'hincrby'];

export class RedisMock {
export class RedisAdapterMock {

private pipelineMethods: any = { exec: jest.fn(asyncFunction) };
private pipelineMethods = { exec: jest.fn(asyncFunction) };

constructor() {
IDENTITY_METHODS.forEach(method => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { UniqueKeysCacheInRedis } from '../UniqueKeysCacheInRedis';
import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock';
import { RedisMock } from '../../../utils/redis/RedisMock';
import { RedisAdapterMock } from './RedisAdapter.mock';
import { RedisAdapter } from '../RedisAdapter';

describe('UNIQUE KEYS CACHE IN REDIS', () => {
Expand Down Expand Up @@ -104,7 +104,7 @@ describe('UNIQUE KEYS CACHE IN REDIS', () => {
});

test('start and stop task', (done) => {
const connection = new RedisMock();
const connection = new RedisAdapterMock();
const key = 'unique_key_post';
const refreshRate = 100;

Expand Down
8 changes: 4 additions & 4 deletions src/trackers/eventTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { IEventsHandler, IEventTracker } from './types';
import { ISettings, SplitIO } from '../types';
import { EVENTS_TRACKER_SUCCESS, ERROR_EVENTS_TRACKER } from '../logger/constants';
import { CONSENT_DECLINED, DROPPED, QUEUED } from '../utils/constants';
import { isStorageSync } from './impressionObserver/utils';
import { isConsumerMode } from '../utils/settingsValidation/mode';

/**
* Event tracker stores events in cache and pass them to the integrations manager if provided.
Expand All @@ -20,8 +20,8 @@ export function eventTrackerFactory(
telemetryCache?: ITelemetryCacheSync | ITelemetryCacheAsync
): IEventTracker {

const log = settings.log;
const isSync = isStorageSync(settings);
const { log, mode } = settings;
const isAsync = isConsumerMode(mode);

function queueEventsCallback(eventData: SplitIO.EventData, tracked: boolean) {
const { eventTypeId, trafficTypeName, key, value, timestamp, properties } = eventData;
Expand Down Expand Up @@ -50,7 +50,7 @@ export function eventTrackerFactory(
return {
track(eventData: SplitIO.EventData, size?: number) {
if (settings.userConsent === CONSENT_DECLINED) {
return isSync ? false : Promise.resolve(false);
return isAsync ? Promise.resolve(false) : false;
}

const tracked = eventsCache.track(eventData, size);
Expand Down
9 changes: 0 additions & 9 deletions src/trackers/impressionObserver/utils.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/utils/settingsValidation/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { merge, get } from '../lang';
import { mode } from './mode';
import { validateMode } from './mode';
import { validateSplitFilters } from './splitFilters';
import { STANDALONE_MODE, OPTIMIZED, LOCALHOST_MODE, DEBUG } from '../constants';
import { validImpressionsMode } from './impressionsMode';
Expand Down Expand Up @@ -146,7 +146,7 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV

// ensure a valid SDK mode
// @ts-ignore, modify readonly prop
withDefaults.mode = mode(withDefaults.core.authorizationKey, withDefaults.mode);
withDefaults.mode = validateMode(withDefaults.core.authorizationKey, withDefaults.mode);

// ensure a valid Storage based on mode defined.
// @ts-ignore, modify readonly prop
Expand Down
9 changes: 8 additions & 1 deletion src/utils/settingsValidation/mode.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { LOCALHOST_MODE, STANDALONE_MODE, PRODUCER_MODE, CONSUMER_MODE, CONSUMER_PARTIAL_MODE } from '../constants';

export function mode(key: string, mode: string) {
export function validateMode(key: string, mode: string) {
// Leaving the comparison as is, in case we change the mode name but not the setting.
if (key === 'localhost') return LOCALHOST_MODE;

if ([STANDALONE_MODE, PRODUCER_MODE, CONSUMER_MODE, CONSUMER_PARTIAL_MODE].indexOf(mode) === -1) throw Error('Invalid mode provided');

return mode;
}

/**
* Storage is async if mode is consumer or partial consumer
*/
export function isConsumerMode(mode: string) {
return CONSUMER_MODE === mode || CONSUMER_PARTIAL_MODE === mode;
}
4 changes: 2 additions & 2 deletions src/utils/settingsValidation/splitFilters.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { CONSUMER_MODE, CONSUMER_PARTIAL_MODE } from '../constants';
import { validateSplits } from '../inputValidation/splits';
import { ISplitFiltersValidation } from '../../dtos/types';
import { SplitIO } from '../../types';
import { ILogger } from '../../logger/types';
import { WARN_SPLITS_FILTER_IGNORED, WARN_SPLITS_FILTER_EMPTY, WARN_SPLITS_FILTER_INVALID, SETTINGS_SPLITS_FILTER, LOG_PREFIX_SETTINGS, ERROR_SETS_FILTER_EXCLUSIVE, WARN_LOWERCASE_FLAGSET, WARN_INVALID_FLAGSET, WARN_FLAGSET_NOT_CONFIGURED } from '../../logger/constants';
import { objectAssign } from '../lang/objectAssign';
import { find, uniq } from '../lang';
import { isConsumerMode } from './mode';

// Split filters metadata.
// Ordered according to their precedency when forming the filter query string: `&names=<values>&prefixes=<values>`
Expand Down Expand Up @@ -149,7 +149,7 @@ export function validateSplitFilters(log: ILogger, maybeSplitFilters: any, mode:
// do nothing if `splitFilters` param is not a non-empty array or mode is not STANDALONE
if (!maybeSplitFilters) return res;
// Warn depending on the mode
if (mode === CONSUMER_MODE || mode === CONSUMER_PARTIAL_MODE) {
if (isConsumerMode(mode)) {
log.warn(WARN_SPLITS_FILTER_IGNORED);
return res;
}
Expand Down
Loading