Comparing version 1.2.9 to 2.0.2
{ | ||
"name": "rox-base", | ||
"version": "1.2.9", | ||
"version": "2.0.2", | ||
"description": "Rollout.io ROX JS SDK Base", | ||
@@ -46,3 +46,2 @@ "author": "Rollout.io <support@rollout.io>", | ||
"loglevel": "^1.4.1", | ||
"lscache": "^1.1.0", | ||
"md5": "^2.2.1", | ||
@@ -61,5 +60,9 @@ "qs": "^6.5.0", | ||
"jest": { | ||
"moduleFileExtensions": ["js"], | ||
"moduleDirectories": ["node_modules"] | ||
"moduleFileExtensions": [ | ||
"js" | ||
], | ||
"moduleDirectories": [ | ||
"node_modules" | ||
] | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
import { hasOverride, getOverride } from '../lib/Overrider'; | ||
import { invokeFlagImpression } from '../lib/FlagImpressionHandler'; | ||
@@ -41,4 +41,4 @@ export default class RoxVariant { | ||
get overridenValue() { | ||
if (hasOverride(this.name)) { | ||
return getOverride(this.name); | ||
if (this.overrider.hasOverride(this.name)) { | ||
return this.overrider.getOverride(this.name); | ||
} | ||
@@ -72,5 +72,9 @@ } | ||
overridingValue: this.overridenValue, | ||
value: this.getValue() | ||
value: this.getValue.length === 1 ? this.getValue(true) : this.getValue(null, true) | ||
}; | ||
} | ||
_flagImpression(value, context) { | ||
invokeFlagImpression(value, this, context); | ||
} | ||
} |
@@ -9,14 +9,24 @@ import { Configuration as configurationRepository, Flags as flagsRepository } from '../repositories'; | ||
this._configurationRepository = this.options.configurationRepository; | ||
this._namespaceStore = new Set(); | ||
} | ||
handleContainer(baseName, container) { | ||
baseName = baseName.length > 0 ? baseName + '.' : ''; | ||
handleContainer(namespace, container) { | ||
if (Object.prototype.toString.call(namespace) !== '[object String]') { | ||
throw new Error('InvalidNamespace: Namespace must be a string (non-nullable).'); | ||
} | ||
if (this._namespaceStore.has(namespace)) { | ||
const err = `InvalidNamespace: A namespace must be unique. A container with the given namesapce ('${namespace}') has already been registered.`; | ||
throw new Error(err); | ||
} else { | ||
this._namespaceStore.add(namespace); | ||
} | ||
for (let prop in container) { | ||
if (container.hasOwnProperty(prop)) { | ||
let label = namespace ? `${namespace}.${prop}` : prop; | ||
let entity = container[prop]; | ||
if (entity._entityType === 'flag' || entity._entityType === 'variant') { | ||
this._flagsRepository.addFlag(`${baseName}${prop}`, entity); | ||
this._flagsRepository.addFlag(label, entity); | ||
} | ||
if (entity._entityType === 'configuration') { | ||
this._configurationRepository.addRemoteConfiguration(`${baseName}${prop}`, entity); | ||
this._configurationRepository.addRemoteConfiguration(label, entity); | ||
} | ||
@@ -23,0 +33,0 @@ } |
@@ -13,2 +13,3 @@ import { CustomProperty } from '../entities'; | ||
import { RoxLogger, ClassRegister, ConfigurationFetcher } from './'; | ||
import { setHandler } from './FlagImpressionHandler'; | ||
@@ -23,3 +24,3 @@ let _deps = { | ||
constructor() { | ||
this.containers = {}; | ||
this.classRegisterer = new _deps.ClassRegister(); | ||
} | ||
@@ -38,6 +39,12 @@ | ||
_deps.DeviceProperties = _deps.DeviceProperties.create ? _deps.DeviceProperties.create() : _deps.DeviceProperties; | ||
this.app_release && _deps.DeviceProperties.setAppRelease(this.app_release); | ||
this.distinct_id && _deps.DeviceProperties.setDistinctId(this.distinct_id); | ||
this.deviceProperties = _deps.DeviceProperties; | ||
this.configurationFetcher = new ConfigurationFetcher(this.appKey, this.deviceProperties, this.devModeSecret); | ||
this.configurationFetcher = new ConfigurationFetcher( | ||
this.appKey, | ||
this.deviceProperties, | ||
this.devModeSecret, | ||
_deps.cacheService | ||
); | ||
_deps | ||
@@ -53,6 +60,8 @@ .getDefaultCustomProperties(this.deviceProperties) | ||
this.fetchIntervalInSec = _options.fetchIntervalInSec; | ||
this.syncCompletionHandler = _options.syncCompletionHandler; | ||
this.configurationFetchedHandler = _options.configurationFetchedHandler; | ||
this.flagImpressionHandler = _options.flagImpressionHandler; | ||
this.app_release = _options.version; | ||
this.distinct_id = _options.distinctId; | ||
this.devModeSecret = _options.devModeSecret; | ||
setHandler(this.flagImpressionHandler); | ||
} | ||
@@ -67,3 +76,7 @@ | ||
if (this.fetchIntervalInSec < minimumIntervalInSec) this.fetchIntervalInSec = minimumIntervalInSec; | ||
this._fetch({ | ||
useCache: false | ||
}); | ||
this.configurationFetcher.dispatchPeriodically({ | ||
handler: this.configurationFetchedHandler, | ||
periodTimeInSec: this.fetchIntervalInSec | ||
@@ -76,4 +89,3 @@ }); | ||
this._fetch({ | ||
useCache: true, | ||
shouldProcess: true | ||
useCache: true | ||
}); | ||
@@ -84,4 +96,3 @@ } | ||
this._fetch({ | ||
useCache: false, | ||
shouldProcess: true | ||
useCache: false | ||
}); | ||
@@ -95,4 +106,7 @@ } | ||
} | ||
if (!this.configurationFetcher) { | ||
return; | ||
} | ||
this.configurationFetcher.dispatch({ | ||
handler: () => this.syncCompletionHandler && this.syncCompletionHandler(this.metadata), | ||
handler: this.configurationFetchedHandler, | ||
options | ||
@@ -103,11 +117,6 @@ }); | ||
register(name, container) { | ||
RoxLogger.debug(`Register container = ${JSON.stringify(container)} with name = ${name}`); | ||
this.containers[name] = container; | ||
new _deps.ClassRegister().handleContainer(name, container); | ||
RoxLogger.debug(`Registering container '${name}' = ${JSON.stringify(container)}`); | ||
this.classRegisterer.handleContainer(name, container); | ||
} | ||
getContainer(name) { | ||
return this.containers[name]; | ||
} | ||
setCustomProperty(name, type, value) { | ||
@@ -117,3 +126,3 @@ customPropertyRepository.add(new CustomProperty(name, type, value)); | ||
unfreeze(namespace) { | ||
unfreeze(namespace, freezeLevel) { | ||
const namespaceFilter = flag => { | ||
@@ -131,7 +140,7 @@ if (!flag.name || typeof namespace !== 'string') return true; | ||
FlagsRepository.flags.filter(namespaceFilter).forEach(flag => { | ||
flag.unfreeze(); | ||
flag.unfreeze(freezeLevel); | ||
}); | ||
ConfigurationRepository.remoteConfigurations.filter(namespaceFilter).forEach(remoteConfiguration => { | ||
remoteConfiguration.unfreeze(); | ||
remoteConfiguration.unfreeze(freezeLevel); | ||
}); | ||
@@ -138,0 +147,0 @@ } |
@@ -8,3 +8,2 @@ import { fetchRemoteConfiguration } from './RequestConfiguration'; | ||
import Config from '../config'; | ||
import Cache from './RoxCache'; | ||
@@ -19,18 +18,16 @@ const CACHE_KEY = Config.get('CLIENT_DATA_CACHE_KEY'); | ||
import { FetcherResults, FetcherStatus } from './FetcherResults'; | ||
class ConfigurationFetcher { | ||
constructor(appKey, deviceProperties, devModeSecret) { | ||
constructor(appKey, deviceProperties, devModeSecret, cacheService) { | ||
this.cacheService = cacheService; | ||
this.appKey = appKey; | ||
this.deviceProperties = deviceProperties; | ||
this.devModeSecret = devModeSecret; | ||
this.lastResponse = null; | ||
} | ||
get hasCachedConfiguration() { | ||
const cached = Cache.get(CACHE_KEY); | ||
if (cached) return true; | ||
return false; | ||
} | ||
runHandler(handler) { | ||
runHandler(handler, data) { | ||
if (handler instanceof Function) { | ||
handler(); | ||
handler(data); | ||
} | ||
@@ -42,19 +39,20 @@ } | ||
var useCache = options.useCache; | ||
var shouldProcess = options.shouldProcess; | ||
const cached = useCache && this.process(this.fetchFromCache()); | ||
let cached = Promise.resolve(); | ||
if (useCache) { | ||
const cachedData = this.fetchFromCache(); | ||
if (cachedData) { | ||
this.lastResponse = cachedData; | ||
cached = this.process(FetcherStatus.appliedFromCache, false, handler, cachedData); | ||
} | ||
} | ||
const networkPromise = this.fetchFromNetwork().then(response => { | ||
this.storeInCache(response); | ||
if (shouldProcess) { | ||
return this.process(response).then(() => this.runHandler(handler)); | ||
} else { | ||
this.runHandler(); | ||
} | ||
const networkPromise = this._dispatch({ handler, storeInCache: true }); | ||
return Promise.all([cached, networkPromise]).catch(err => { | ||
this.runHandler(handler, new FetcherResults(FetcherStatus.errorFetchFailed, null, false, err)); | ||
}); | ||
return Promise.all([cached, networkPromise]).catch(() => {}); | ||
} | ||
dispatchPeriodically({ periodTimeInSec }) { | ||
dispatchPeriodically({ handler, periodTimeInSec }) { | ||
if (isdispatchPeriodicallyRuning) { | ||
@@ -69,6 +67,19 @@ RoxLogger.debug('Dispatch Periodically already running'); | ||
setInterval(() => { | ||
this.fetchFromNetwork().then(response => this.process(response)); | ||
this._dispatch({ handler }); | ||
}, periodTimeInSec * 1000); | ||
} | ||
_dispatch({ handler, storeInCache }) { | ||
return this.fetchFromNetwork() | ||
.then(response => { | ||
const hasChanges = this.isNewResponse(response); | ||
storeInCache && this.storeInCache(response); | ||
this.lastResponse = response; | ||
return this.process(FetcherStatus.appliedFromNetwork, hasChanges, handler, response); | ||
}) | ||
.catch(err => { | ||
this.runHandler(handler, new FetcherResults(FetcherStatus.errorFetchFailed, null, false, err)); | ||
}); | ||
} | ||
fetchFromNetwork() { | ||
@@ -87,3 +98,3 @@ RoxLogger.debug(`fetch from network for appKey ${this.appKey}`); | ||
RoxLogger.debug('fetch From Cache'); | ||
const cached = Cache.get(CACHE_KEY); | ||
const cached = this.cacheService.get(CACHE_KEY); | ||
let parsed; | ||
@@ -105,11 +116,19 @@ if (cached) { | ||
RoxLogger.debug(`Store in cache response = ${JSON.stringify(response)}`); | ||
Cache.set(CACHE_KEY, JSON.stringify(response), CACHE_TTL); | ||
this.cacheService.set(CACHE_KEY, JSON.stringify(response), CACHE_TTL); | ||
} | ||
process(payload) { | ||
process(source, hasChanges, handler, payload) { | ||
if (payload) { | ||
this.calculatePayload(this.parsePayload(payload)); | ||
return Promise.resolve(); | ||
const parser = this.calculatePayload(this.parsePayload(payload)); | ||
if (parser) { | ||
return new Promise(resolve => { | ||
const fetcherResults = new FetcherResults(source, parser.signedDate(), hasChanges); | ||
this.runHandler(handler, fetcherResults); | ||
resolve(); | ||
}); | ||
} else { | ||
return Promise.reject('Failed to parse configuration'); | ||
} | ||
} | ||
return Promise.reject(); | ||
return Promise.reject('Empty configuration'); | ||
} | ||
@@ -140,6 +159,10 @@ | ||
return; | ||
return parser; | ||
} | ||
isNewResponse(response) { | ||
return JSON.stringify(this.lastResponse) !== JSON.stringify(response); | ||
} | ||
} | ||
export default ConfigurationFetcher; |
@@ -1,6 +0,6 @@ | ||
import Cache from '../lib/RoxCache'; | ||
import uuid from 'uuid/v4'; | ||
export default class DeviceProperties { | ||
constructor() { | ||
constructor(cacheService) { | ||
this.cacheService = cacheService; | ||
this.distinct_id = this.generateDistinctId(); | ||
@@ -11,3 +11,3 @@ this.app_release = '0.0'; | ||
setDistinctId (id) { | ||
setDistinctId(id) { | ||
this.distinctIdSetExplicitly = true; | ||
@@ -17,11 +17,11 @@ this.distinct_id = id; | ||
setAppRelease (appRelease) { | ||
this.app_release = appRelease | ||
setAppRelease(appRelease) { | ||
this.app_release = appRelease; | ||
} | ||
generateDistinctId() { | ||
let distinct_id = Cache.get('distinctId'); | ||
let distinct_id = this.cacheService.get('distinctId'); | ||
if (!distinct_id) { | ||
distinct_id = uuid(); | ||
Cache.set('distinctId', distinct_id); | ||
this.cacheService.set('distinctId', distinct_id); | ||
} | ||
@@ -28,0 +28,0 @@ return distinct_id; |
@@ -1,2 +0,1 @@ | ||
import * as Overrider from './Overrider'; | ||
export { default as ConfigurationFetcher } from './ConfigurationFetcher'; | ||
@@ -8,2 +7,1 @@ export { default as RuntimeRegistry } from './RuntimeRegistry'; | ||
export { default as createRoxClient } from './Client'; | ||
export { Overrider }; |
@@ -7,3 +7,3 @@ import RoxConfigurationRepository from '../repositories/RoxConfigurationRepository'; | ||
let cp = []; | ||
for (let customProperties of RoxCustomRepository.items) { | ||
RoxCustomRepository.items.forEach(customProperties => { | ||
cp.push({ | ||
@@ -14,3 +14,3 @@ name: customProperties.name, | ||
}); | ||
} | ||
}); | ||
return cp; | ||
@@ -21,3 +21,3 @@ } | ||
let featureFlags = []; | ||
for (let flag of RoxFlagRepository.items) { | ||
RoxFlagRepository.items.forEach(flag => { | ||
featureFlags.push({ | ||
@@ -28,4 +28,3 @@ name: flag.name, | ||
}); | ||
} | ||
}); | ||
return featureFlags; | ||
@@ -36,3 +35,3 @@ } | ||
let remoteConfigurations = []; | ||
for (let remoteConfiguration of RoxConfigurationRepository.items) { | ||
RoxConfigurationRepository.items.forEach(remoteConfiguration => { | ||
remoteConfigurations.push({ | ||
@@ -44,5 +43,5 @@ name: remoteConfiguration.name, | ||
}); | ||
} | ||
}); | ||
return remoteConfigurations; | ||
} | ||
} |
@@ -10,3 +10,4 @@ import ExperimentsParser from './ExperimentsParser'; | ||
TARGET_GROUPS: 'targetGroups', | ||
REMOTE_CONFIGURATIONS: 'remoteVariables' | ||
REMOTE_CONFIGURATIONS: 'remoteVariables', | ||
SIGNED_DATE: 'signed_date' | ||
}; | ||
@@ -34,2 +35,3 @@ | ||
this._parseRemoteConfigurations(data[terms.REMOTE_CONFIGURATIONS]); | ||
this._signedDate = new Date(this._json[terms.SIGNED_DATE]); | ||
@@ -51,2 +53,6 @@ return this; | ||
signedDate() { | ||
return this._signedDate; | ||
} | ||
_extractInnerJson(json) { | ||
@@ -53,0 +59,0 @@ return JSON.parse(json[terms.DATA_CONTAINER]); |
@@ -6,16 +6,16 @@ class CustomPropertyRepository { | ||
add (property) { | ||
add(property) { | ||
this.store.set(property.name, property); | ||
} | ||
get (name) { | ||
get(name) { | ||
return this.store.get(name); | ||
} | ||
clear () { | ||
this.store.clear() | ||
clear() { | ||
this.store.clear(); | ||
} | ||
get items() { | ||
return this.store.values(); | ||
return Array.from(this.store.values()); | ||
} | ||
@@ -22,0 +22,0 @@ } |
@@ -20,4 +20,8 @@ class ExperimentsRepository { | ||
get experiments() { | ||
return Object.keys(this.map).map(t => this.map[t]) | ||
return Object.keys(this.map).map(t => this.map[t]); | ||
} | ||
experimentForFlag(flag) { | ||
return this.experiments.find(e => e.flags && e.flags.some(f => f.name === flag.name)); | ||
} | ||
} | ||
@@ -24,0 +28,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
6
1489754
10328
5
- Removedlscache@^1.1.0
- Removedlscache@1.3.2(transitive)