Skip to content
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
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"plugin:compat/recommended"
],
"rules": {
"no-restricted-syntax": ["error", "ForOfStatement", "ForInStatement", "ArrayPattern"],
"no-restricted-syntax": ["error", "ForOfStatement", "ForInStatement"],
"compat/compat": ["error", "defaults, not ie < 10, not node < 6"],
"no-throw-literal": "error"
},
Expand Down
37 changes: 23 additions & 14 deletions src/evaluator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ const treatmentException = {
config: null
};

function treatmentsException(splitNames: string[]) {
const evaluations: Record<string, IEvaluationResult> = {};
splitNames.forEach(splitName => {
evaluations[splitName] = treatmentException;
});
return evaluations;
}

export function evaluateFeature(
log: ILogger,
key: SplitIO.SplitKey,
Expand All @@ -27,10 +35,8 @@ export function evaluateFeature(
try {
stringifiedSplit = storage.splits.getSplit(splitName);
} catch (e) {
// the only scenario where getSplit can throw an error is when the storage
// is redis and there is a connection issue and we can't retrieve the split
// to be evaluated
return Promise.resolve(treatmentException);
// Exception on sync `getSplit` storage. Not possible ATM with InMemory and InLocal storages.
return treatmentException;
}

if (thenable(stringifiedSplit)) {
Expand All @@ -40,7 +46,11 @@ export function evaluateFeature(
key,
attributes,
storage,
));
)).catch(
// Exception on async `getSplit` storage. For example, when the storage is redis or
// pluggable and there is a connection issue and we can't retrieve the split to be evaluated
() => treatmentException
);
}

return getEvaluation(
Expand All @@ -60,22 +70,21 @@ export function evaluateFeatures(
storage: IStorageSync | IStorageAsync,
): MaybeThenable<Record<string, IEvaluationResult>> {
let stringifiedSplits;
const evaluations: Record<string, IEvaluationResult> = {};

try {
stringifiedSplits = storage.splits.getSplits(splitNames);
} catch (e) {
// the only scenario where `getSplits` can throw an error is when the storage
// is redis and there is a connection issue and we can't retrieve the split
// to be evaluated
splitNames.forEach(splitName => {
evaluations[splitName] = treatmentException;
});
return Promise.resolve(evaluations);
// Exception on sync `getSplits` storage. Not possible ATM with InMemory and InLocal storages.
return treatmentsException(splitNames);
}

return (thenable(stringifiedSplits)) ?
stringifiedSplits.then(splits => getEvaluations(log, splitNames, splits, key, attributes, storage)) :
stringifiedSplits.then(splits => getEvaluations(log, splitNames, splits, key, attributes, storage))
.catch(() => {
// Exception on async `getSplits` storage. For example, when the storage is redis or
// pluggable and there is a connection issue and we can't retrieve the split to be evaluated
return treatmentsException(splitNames);
}) :
getEvaluations(log, splitNames, stringifiedSplits, key, attributes, storage);
}

Expand Down
2 changes: 1 addition & 1 deletion src/evaluator/value/sanitize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function sanitizeString(val: any): string | undefined {
return str ? str : undefined;
}

function sanitizeArray(val: any): number[] | string[] | undefined {
function sanitizeArray(val: any): string[] | undefined {
const arr = Array.isArray(val) ? uniq(val.map(e => e + '')) : [];
return arr.length ? arr : undefined;
}
Expand Down
21 changes: 4 additions & 17 deletions src/storages/AbstractSplitsCacheSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,13 @@ export default abstract class AbstractSplitsCacheSync implements ISplitsCacheSyn
abstract addSplit(name: string, split: string): boolean;

addSplits(entries: [string, string][]): boolean[] {
const results: boolean[] = [];

entries.forEach(keyValuePair => {
results.push(this.addSplit(keyValuePair[0], keyValuePair[1]));
});

return results;
return entries.map(keyValuePair => this.addSplit(keyValuePair[0], keyValuePair[1]));
}

abstract removeSplit(name: string): number

removeSplits(names: string[]): number {
let len = names.length;
let counter = 0;

for (let i = 0; i < len; i++) {
counter += this.removeSplit(names[i]);
}
abstract removeSplit(name: string): boolean

return counter;
removeSplits(names: string[]): boolean[] {
return names.map(name => this.removeSplit(name));
}

abstract getSplit(name: string): string | null
Expand Down
8 changes: 8 additions & 0 deletions src/storages/KeyBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ export default class KeyBuilder {
return startsWith(key, `${this.prefix}.split.`);
}

buildSplitKeyPrefix() {
return `${this.prefix}.split.`;
}

buildSplitsWithSegmentCountKey() {
return `${this.prefix}.splits.usingSegments`;
}

buildSegmentNameKey(segmentName: string) {
return `${this.prefix}.segment.${segmentName}`;
}
Expand Down
4 changes: 0 additions & 4 deletions src/storages/KeyBuilderCS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ export default class KeyBuilderCS extends KeyBuilder {
return builtSegmentKeyName.substr(prefix.length);
}

buildSplitsWithSegmentCountKey() {
return `${this.prefix}.splits.usingSegments`;
}

buildLastUpdatedKey() {
return `${this.prefix}.splits.lastUpdated`;
}
Expand Down
2 changes: 1 addition & 1 deletion src/storages/KeyBuilderSS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default class KeyBuilderSS extends KeyBuilder {
// }

searchPatternForSplitKeys() {
return `${this.prefix}.split.*`;
return `${this.buildSplitKeyPrefix()}*`;
}

// NOT USED
Expand Down
4 changes: 1 addition & 3 deletions src/storages/dataLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ export function dataLoaderFactory(preloadedData: SplitIO.PreloadedData): DataLoa
storage.splits.setChangeNumber(since);

// splitsData in an object where the property is the split name and the pertaining value is a stringified json of its data
Object.keys(splitsData).forEach(splitName => {
storage.splits.addSplit(splitName, splitsData[splitName]);
});
storage.splits.addSplits(Object.keys(splitsData).map(splitName => [splitName, splitsData[splitName]]));

// add mySegments data
let mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userId];
Expand Down
8 changes: 4 additions & 4 deletions src/storages/inLocalStorage/MySegmentsCacheInLocal.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ILogger } from '../../logger/types';
import AbstractSegmentsCacheSync from '../AbstractSegmentsCacheSync';
import KeyBuilderCS from '../KeyBuilderCS';
import { logPrefix, DEFINED } from './constants';
import { LOG_PREFIX, DEFINED } from './constants';

export default class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {

Expand All @@ -20,7 +20,7 @@ export default class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
* @NOTE this method is not being used at the moment.
*/
clear() {
this.log.info(logPrefix + 'Flushing MySegments data from localStorage');
this.log.info(LOG_PREFIX + 'Flushing MySegments data from localStorage');

// We cannot simply call `localStorage.clear()` since that implies removing user items from the storage
// We could optimize next sentence, since it implies iterating over all localStorage items
Expand All @@ -34,7 +34,7 @@ export default class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
localStorage.setItem(segmentKey, DEFINED);
return true;
} catch (e) {
this.log.error(logPrefix + e);
this.log.error(LOG_PREFIX + e);
return false;
}
}
Expand All @@ -46,7 +46,7 @@ export default class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
localStorage.removeItem(segmentKey);
return true;
} catch (e) {
this.log.error(logPrefix + e);
this.log.error(LOG_PREFIX + e);
return false;
}
}
Expand Down
26 changes: 13 additions & 13 deletions src/storages/inLocalStorage/SplitsCacheInLocal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import AbstractSplitsCacheSync, { usesSegments } from '../AbstractSplitsCacheSyn
import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang';
import KeyBuilderCS from '../KeyBuilderCS';
import { ILogger } from '../../logger/types';
import { logPrefix } from './constants';
import { LOG_PREFIX } from './constants';

/**
* ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
Expand Down Expand Up @@ -52,7 +52,7 @@ export default class SplitsCacheInLocal extends AbstractSplitsCacheSync {
}
}
} catch (e) {
this.log.error(logPrefix + e);
this.log.error(LOG_PREFIX + e);
}
}

Expand All @@ -72,7 +72,7 @@ export default class SplitsCacheInLocal extends AbstractSplitsCacheSync {
}
}
} catch (e) {
this.log.error(logPrefix + e);
this.log.error(LOG_PREFIX + e);
}
}

Expand All @@ -82,7 +82,7 @@ export default class SplitsCacheInLocal extends AbstractSplitsCacheSync {
* We cannot simply call `localStorage.clear()` since that implies removing user items from the storage.
*/
clear() {
this.log.info(logPrefix + 'Flushing Splits data from localStorage');
this.log.info(LOG_PREFIX + 'Flushing Splits data from localStorage');

// collect item keys
const len = localStorage.length;
Expand Down Expand Up @@ -114,23 +114,23 @@ export default class SplitsCacheInLocal extends AbstractSplitsCacheSync {

return true;
} catch (e) {
this.log.error(logPrefix + e);
this.log.error(LOG_PREFIX + e);
return false;
}
}

removeSplit(name: string): number {
removeSplit(name: string): boolean {
try {
const split = this.getSplit(name);
localStorage.removeItem(this.keys.buildSplitKey(name));

const parsedSplit = JSON.parse(split as string);
this._decrementCounts(parsedSplit);

return 1;
return true;
} catch (e) {
this.log.error(logPrefix + e);
return 0;
this.log.error(LOG_PREFIX + e);
return false;
}
}

Expand All @@ -147,14 +147,14 @@ export default class SplitsCacheInLocal extends AbstractSplitsCacheSync {

// when using a new split query, we must update it at the store
if (this.updateNewFilter) {
this.log.info(logPrefix + 'Split filter query was modified. Updating cache.');
this.log.info(LOG_PREFIX + 'Split filter query was modified. Updating cache.');
const queryKey = this.keys.buildSplitsFilterQueryKey();
const queryString = this.splitFiltersValidation.queryString;
try {
if (queryString) localStorage.setItem(queryKey, queryString);
else localStorage.removeItem(queryKey);
} catch (e) {
this.log.error(logPrefix + e);
this.log.error(LOG_PREFIX + e);
}
this.updateNewFilter = false;
}
Expand All @@ -166,7 +166,7 @@ export default class SplitsCacheInLocal extends AbstractSplitsCacheSync {
this.hasSync = true;
return true;
} catch (e) {
this.log.error(logPrefix + e);
this.log.error(LOG_PREFIX + e);
return false;
}
}
Expand Down Expand Up @@ -273,7 +273,7 @@ export default class SplitsCacheInLocal extends AbstractSplitsCacheSync {
});
}
} catch (e) {
this.log.error(logPrefix + e);
this.log.error(LOG_PREFIX + e);
}
}
// if the filter didn't change, nothing is done
Expand Down
2 changes: 1 addition & 1 deletion src/storages/inLocalStorage/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const logPrefix = 'storage:localstorage: ';
export const LOG_PREFIX = 'storage:localstorage: ';
export const DEFINED = '1';
4 changes: 2 additions & 2 deletions src/storages/inLocalStorage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import MySegmentsCacheInMemory from '../inMemory/MySegmentsCacheInMemory';
import SplitsCacheInMemory from '../inMemory/SplitsCacheInMemory';
import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
import { logPrefix } from './constants';
import { LOG_PREFIX } from './constants';

export interface InLocalStorageOptions {
prefix?: string
Expand All @@ -27,7 +27,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}) {

// Fallback to InMemoryStorage if LocalStorage API is not available
if (!isLocalStorageAvailable()) {
params.log.warn(logPrefix + 'LocalStorage API is unavailable. Fallbacking into default MEMORY storage');
params.log.warn(LOG_PREFIX + 'LocalStorage API is unavailable. Fallbacking into default MEMORY storage');
return InMemoryStorageCSFactory(params);
}

Expand Down
6 changes: 3 additions & 3 deletions src/storages/inMemory/SplitsCacheInMemory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default class SplitsCacheInMemory extends AbstractSplitsCacheSync {
}
}

removeSplit(name: string): number {
removeSplit(name: string): boolean {
const split = this.getSplit(name);
if (split) {
// Delete the Split
Expand All @@ -74,9 +74,9 @@ export default class SplitsCacheInMemory extends AbstractSplitsCacheSync {
// Update the segments count.
if (usesSegments(parsedSplit)) this.splitsWithSegmentsCount--;

return 1;
return true;
} else {
return 0;
return false;
}
}

Expand Down
5 changes: 2 additions & 3 deletions src/storages/inRedis/EventsCacheInRedis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import KeyBuilderSS from '../KeyBuilderSS';
import { Redis } from 'ioredis';
import { SplitIO } from '../../types';
import { ILogger } from '../../logger/types';

const logPrefix = 'storage:redis: ';
import { LOG_PREFIX } from './constants';

export default class EventsCacheInRedis implements IEventsCacheAsync {

Expand Down Expand Up @@ -33,7 +32,7 @@ export default class EventsCacheInRedis implements IEventsCacheAsync {
// We use boolean values to signal successful queueing
.then(() => true)
.catch(err => {
this.log.error(logPrefix + `Error adding event to queue: ${err}.`);
this.log.error(LOG_PREFIX + `Error adding event to queue: ${err}.`);
return false;
});
}
Expand Down
Loading