Skip to content

Commit

Permalink
refactor: Removed cloneDeep dependency from the sdk package (#415)
Browse files Browse the repository at this point in the history
Summary:

To reduce package size, we are gradually removing lodash library. This PR removes lodash.cloneDeep from optimizely-sdk package. It is still part of the repository but it is only being used to run tests. lodash has been removed from dependencies and moved to dev-dependencies in package.json

Test plan:

All unit tests and Full Stack compatibility tests pass after this change

Co-authored-by: zashraf1985 <[email protected]>
  • Loading branch information
fayyazarshad and zashraf1985 authored Mar 9, 2020
1 parent 026a21c commit 5cf5ddc
Show file tree
Hide file tree
Showing 13 changed files with 51 additions and 50 deletions.
10 changes: 5 additions & 5 deletions packages/optimizely-sdk/lib/core/bucketer/index.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe('lib/core/bucketer', function() {

describe('return values for bucketing (excluding groups)', function() {
beforeEach(function() {
configObj = projectConfig.createProjectConfig(testData);
configObj = projectConfig.createProjectConfig(cloneDeep(testData));
bucketerParams = {
experimentId: configObj.experiments[0].id,
experimentKey: configObj.experiments[0].key,
Expand Down Expand Up @@ -103,7 +103,7 @@ describe('lib/core/bucketer', function() {
describe('return values for bucketing (including groups)', function() {
var bucketerStub;
beforeEach(function() {
configObj = projectConfig.createProjectConfig(testData);
configObj = projectConfig.createProjectConfig(cloneDeep(testData));
bucketerParams = {
experimentId: configObj.experiments[0].id,
experimentKey: configObj.experiments[0].key,
Expand Down Expand Up @@ -284,7 +284,7 @@ describe('lib/core/bucketer', function() {

describe('when the bucket value falls into empty traffic allocation ranges', function() {
beforeEach(function() {
configObj = projectConfig.createProjectConfig(testData);
configObj = projectConfig.createProjectConfig(cloneDeep(testData));
bucketerParams = {
experimentId: configObj.experiments[0].id,
experimentKey: configObj.experiments[0].key,
Expand Down Expand Up @@ -314,7 +314,7 @@ describe('lib/core/bucketer', function() {

describe('when the traffic allocation has invalid variation ids', function() {
beforeEach(function() {
configObj = projectConfig.createProjectConfig(testData);
configObj = projectConfig.createProjectConfig(cloneDeep(testData));
bucketerParams = {
experimentId: configObj.experiments[0].id,
experimentKey: configObj.experiments[0].key,
Expand Down Expand Up @@ -371,7 +371,7 @@ describe('lib/core/bucketer', function() {
});

beforeEach(function() {
configObj = projectConfig.createProjectConfig(testData);
configObj = projectConfig.createProjectConfig(cloneDeep(testData));
bucketerParams = {
trafficAllocationConfig: configObj.experiments[0].trafficAllocation,
variationIdMap: configObj.variationIdMap,
Expand Down
7 changes: 3 additions & 4 deletions packages/optimizely-sdk/lib/core/decision_service/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,14 +300,13 @@ DecisionService.prototype.__saveUserProfile = function(experiment, variation, us
}

try {
var newBucketMap = fns.cloneDeep(experimentBucketMap);
newBucketMap[experiment.id] = {
variation_id: variation.id,
experimentBucketMap[experiment.id] = {
variation_id: variation.id
};

this.userProfileService.save({
user_id: userId,
experiment_bucket_map: newBucketMap,
experiment_bucket_map: experimentBucketMap,
});

this.logger.log(
Expand Down
18 changes: 9 additions & 9 deletions packages/optimizely-sdk/lib/core/decision_service/index.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var errorHandler = require('../../plugins/error_handler');
var bucketer = require('../bucketer');
var DecisionService = require('./');
var enums = require('../../utils/enums');
var fns = require('../../utils/fns');
var cloneDeep = require('lodash/cloneDeep');
var logger = require('../../plugins/logger');
var projectConfig = require('../project_config');
var sprintf = require('@optimizely/js-sdk-utils').sprintf;
Expand All @@ -40,7 +40,7 @@ var DECISION_SOURCES = enums.DECISION_SOURCES;

describe('lib/core/decision_service', function() {
describe('APIs', function() {
var configObj = projectConfig.createProjectConfig(testData);
var configObj = projectConfig.createProjectConfig(cloneDeep(testData));
var decisionServiceInstance;
var mockLogger = logger.createLogger({ logLevel: LOG_LEVEL.INFO });
var bucketerStub;
Expand Down Expand Up @@ -860,7 +860,7 @@ describe('lib/core/decision_service', function() {
'control'
);
assert.strictEqual(didSetVariation, true);
var newDatafile = fns.cloneDeep(testData);
var newDatafile = cloneDeep(testData);
// Remove 'control' variation from variations, traffic allocation, and datafile forcedVariations.
newDatafile.experiments[0].variations = [
{
Expand Down Expand Up @@ -892,7 +892,7 @@ describe('lib/core/decision_service', function() {
'control'
);
assert.strictEqual(didSetVariation, true);
var newConfigObj = projectConfig.createProjectConfig(testDataWithFeatures);
var newConfigObj = projectConfig.createProjectConfig(cloneDeep(testDataWithFeatures));
var forcedVar = decisionServiceInstance.getForcedVariation(newConfigObj, 'testExperiment', 'user1');
assert.strictEqual(forcedVar, null);
});
Expand All @@ -917,7 +917,7 @@ describe('lib/core/decision_service', function() {

// TODO: Move tests that test methods of Optimizely to lib/optimizely/index.tests.js
describe('when a bucketingID is provided', function() {
var configObj = projectConfig.createProjectConfig(testData);
var configObj = projectConfig.createProjectConfig(cloneDeep(testData));
var createdLogger = logger.createLogger({
logLevel: LOG_LEVEL.DEBUG,
logToConsole: false,
Expand All @@ -926,7 +926,7 @@ describe('lib/core/decision_service', function() {
beforeEach(function() {
optlyInstance = new Optimizely({
clientEngine: 'node-sdk',
datafile: testData,
datafile: cloneDeep(testData),
jsonSchemaValidator: jsonSchemaValidator,
isValidInstance: true,
logger: createdLogger,
Expand Down Expand Up @@ -1050,7 +1050,7 @@ describe('lib/core/decision_service', function() {

beforeEach(function() {
sinon.stub(mockLogger, 'log');
configObj = projectConfig.createProjectConfig(testData);
configObj = projectConfig.createProjectConfig(cloneDeep(testData));
decisionService = DecisionService.createDecisionService({
logger: mockLogger,
});
Expand Down Expand Up @@ -1088,7 +1088,7 @@ describe('lib/core/decision_service', function() {
var sandbox;
var mockLogger = logger.createLogger({ logLevel: LOG_LEVEL.INFO });
beforeEach(function() {
configObj = projectConfig.createProjectConfig(testDataWithFeatures);
configObj = projectConfig.createProjectConfig(cloneDeep(testDataWithFeatures));
sandbox = sinon.sandbox.create();
sandbox.stub(mockLogger, 'log');
decisionServiceInstance = DecisionService.createDecisionService({
Expand Down Expand Up @@ -1978,7 +1978,7 @@ describe('lib/core/decision_service', function() {
var __buildBucketerParamsSpy;

beforeEach(function() {
configObj = projectConfig.createProjectConfig(testDataWithFeatures);
configObj = projectConfig.createProjectConfig(cloneDeep(testDataWithFeatures));
feature = configObj.featureKeyMap.test_feature;
decisionService = DecisionService.createDecisionService({
logger: logger.createLogger({ logLevel: LOG_LEVEL.INFO }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/
var assert = require('chai').assert;

var cloneDeep = require('lodash/cloneDeep');
var datafile = require('../../tests/test_data').getTestProjectConfigWithFeatures();
var projectConfig = require('../project_config');
var optimizelyConfig = require('./index');
Expand All @@ -37,7 +37,7 @@ describe('lib/core/optimizely_config', function() {
var optimizelyConfigObject;
var projectConfigObject;
beforeEach(function() {
projectConfigObject = projectConfig.createProjectConfig(datafile);
projectConfigObject = projectConfig.createProjectConfig(cloneDeep(datafile));
optimizelyConfigObject = optimizelyConfig.getOptimizelyConfig(projectConfigObject);
});

Expand Down
8 changes: 4 additions & 4 deletions packages/optimizely-sdk/lib/core/project_config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module.exports = {
* @return {Object} Object representing project configuration
*/
createProjectConfig: function(datafile) {
var projectConfig = fns.cloneDeep(datafile);
var projectConfig = fns.assign({}, datafile);

/*
* Conditions of audiences in projectConfig.typedAudiences are not
Expand All @@ -53,16 +53,16 @@ module.exports = {

var experiments;
Object.keys(projectConfig.groupIdMap || {}).forEach(function(Id) {
experiments = fns.cloneDeep(projectConfig.groupIdMap[Id].experiments);
experiments = projectConfig.groupIdMap[Id].experiments;
(experiments || []).forEach(function(experiment) {
projectConfig.experiments.push(fns.assign(experiment, { groupId: Id }));
});
});

projectConfig.rolloutIdMap = fns.keyBy(projectConfig.rollouts || [], 'id');
jsSdkUtils.objectValues(projectConfig.rolloutIdMap || {}).forEach(function(rollout) {
jsSdkUtils.objectValues(projectConfig.rolloutIdMap || {}).forEach(function (rollout) {
(rollout.experiments || []).forEach(function(experiment) {
projectConfig.experiments.push(fns.cloneDeep(experiment));
projectConfig.experiments.push(experiment);
// Creates { <variationKey>: <variation> } map inside of the experiment
experiment.variationKeyMap = fns.keyBy(experiment.variations, 'key');
});
Expand Down
21 changes: 11 additions & 10 deletions packages/optimizely-sdk/lib/core/project_config/index.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ var logging = require('@optimizely/js-sdk-logging');

var logger = logging.getLogger();

var _ = require('lodash/core');
var forEach = require('lodash/forEach');
var cloneDeep = require('lodash/cloneDeep');
var fns = require('../../utils/fns');
var chai = require('chai');
var assert = chai.assert;
Expand All @@ -40,8 +41,8 @@ describe('lib/core/project_config', function() {
var testData = testDatafile.getTestProjectConfig();
var configObj = projectConfig.createProjectConfig(testData);

_.forEach(testData.audiences, function(audience) {
audience.conditions = JSON.parse(audience.conditions);
forEach(testData.audiences, function(audience) {
audience.conditions = audience.conditions;
});

assert.strictEqual(configObj.accountId, testData.accountId);
Expand All @@ -59,14 +60,14 @@ describe('lib/core/project_config', function() {
assert.deepEqual(configObj.groupIdMap, expectedGroupIdMap);

var expectedExperiments = testData.experiments;
_.forEach(configObj.groupIdMap, function(group, Id) {
_.forEach(group.experiments, function(experiment) {
forEach(configObj.groupIdMap, function(group, Id) {
forEach(group.experiments, function(experiment) {
experiment.groupId = Id;
expectedExperiments.push(experiment);
});
});

_.forEach(expectedExperiments, function(experiment) {
forEach(expectedExperiments, function(experiment) {
experiment.variationKeyMap = fns.keyBy(experiment.variations, 'key');
});

Expand Down Expand Up @@ -242,12 +243,12 @@ describe('lib/core/project_config', function() {
});

describe('projectConfig helper methods', function() {
var testData = testDatafile.getTestProjectConfig();
var testData = cloneDeep(testDatafile.getTestProjectConfig());
var configObj;
var createdLogger = loggerPlugin.createLogger({ logLevel: LOG_LEVEL.INFO });

beforeEach(function() {
configObj = projectConfig.createProjectConfig(testData);
configObj = projectConfig.createProjectConfig(cloneDeep(testData));
sinon.stub(createdLogger, 'log');
});

Expand Down Expand Up @@ -630,14 +631,14 @@ describe('lib/core/project_config', function() {

describe('#getExperimentAudienceConditions', function() {
it('should retrieve audiences for valid experiment key', function() {
configObj = projectConfig.createProjectConfig(testData);
configObj = projectConfig.createProjectConfig(cloneDeep(testData));
assert.deepEqual(projectConfig.getExperimentAudienceConditions(configObj, testData.experiments[1].key), [
'11154',
]);
});

it('should throw error for invalid experiment key', function() {
configObj = projectConfig.createProjectConfig(testData);
configObj = projectConfig.createProjectConfig(cloneDeep(testData));
assert.throws(function() {
projectConfig.getExperimentAudienceConditions(configObj, 'invalidExperimentKey');
}, sprintf(ERROR_MESSAGES.INVALID_EXPERIMENT_KEY, 'PROJECT_CONFIG', 'invalidExperimentKey'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var assert = require('chai').assert;
var datafileManager = require('@optimizely/js-sdk-datafile-manager');
var logging = require('@optimizely/js-sdk-logging');
var sinon = require('sinon');
var cloneDeep = require('lodash/cloneDeep');
var sprintf = require('@optimizely/js-sdk-utils').sprintf;
var enums = require('../../utils/enums');
var jsonSchemaValidator = require('../../utils/json_schema_validator');
Expand Down Expand Up @@ -156,7 +157,7 @@ describe('lib/core/project_config/project_config_manager', function() {
it('should return a valid datafile from getConfig and resolve onReady with a successful result', function() {
var configWithFeatures = testData.getTestProjectConfigWithFeatures();
var manager = new projectConfigManager.ProjectConfigManager({
datafile: configWithFeatures,
datafile: cloneDeep(configWithFeatures),
});
assert.deepEqual(manager.getConfig(), projectConfig.createProjectConfig(configWithFeatures));
return manager.onReady().then(function(result) {
Expand All @@ -180,8 +181,9 @@ describe('lib/core/project_config/project_config_manager', function() {

describe('with a datafile manager', function() {
it('passes the correct options to datafile manager', function() {
var config = testData.getTestProjectConfig()
new projectConfigManager.ProjectConfigManager({
datafile: testData.getTestProjectConfig(),
datafile: config,
sdkKey: '12345',
datafileOptions: {
autoUpdate: true,
Expand All @@ -192,7 +194,7 @@ describe('lib/core/project_config/project_config_manager', function() {
sinon.assert.calledWithExactly(
datafileManager.HttpPollingDatafileManager,
sinon.match({
datafile: testData.getTestProjectConfig(),
datafile: config,
sdkKey: '12345',
autoUpdate: true,
updateInterval: 10000,
Expand All @@ -206,7 +208,7 @@ describe('lib/core/project_config/project_config_manager', function() {
datafileManager.HttpPollingDatafileManager.returns({
start: sinon.stub(),
stop: sinon.stub(),
get: sinon.stub().returns(configWithFeatures),
get: sinon.stub().returns(cloneDeep(configWithFeatures)),
on: sinon.stub().returns(function() {}),
onReady: sinon.stub().returns(Promise.resolve()),
});
Expand All @@ -233,7 +235,7 @@ describe('lib/core/project_config/project_config_manager', function() {
});
nextDatafile.revision = '36';
var fakeDatafileManager = datafileManager.HttpPollingDatafileManager.getCall(0).returnValue;
fakeDatafileManager.get.returns(nextDatafile);
fakeDatafileManager.get.returns(cloneDeep(nextDatafile));
var updateListener = fakeDatafileManager.on.getCall(0).args[1];
updateListener({ datafile: nextDatafile });
assert.deepEqual(manager.getConfig(), projectConfig.createProjectConfig(nextDatafile));
Expand Down
1 change: 0 additions & 1 deletion packages/optimizely-sdk/lib/index.browser.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ var eventProcessorConfigValidator = require('./utils/event_processor_config_vali

var chai = require('chai');
var assert = chai.assert;
var find = require('lodash/find');
var sinon = require('sinon');

var LocalStoragePendingEventsDispatcher = eventProcessor.LocalStoragePendingEventsDispatcher;
Expand Down
7 changes: 4 additions & 3 deletions packages/optimizely-sdk/lib/optimizely/index.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,10 @@ describe('lib/optimizely', function() {
});

it('passes datafile, datafileOptions, sdkKey, and other options to the project config manager', function() {
var config = testData.getTestProjectConfig();
new Optimizely({
clientEngine: 'node-sdk',
datafile: testData.getTestProjectConfig(),
datafile: config,
datafileOptions: {
autoUpdate: true,
updateInterval: 2 * 60 * 1000,
Expand All @@ -239,7 +240,7 @@ describe('lib/optimizely', function() {
});
sinon.assert.calledOnce(projectConfigManager.ProjectConfigManager);
sinon.assert.calledWithExactly(projectConfigManager.ProjectConfigManager, {
datafile: testData.getTestProjectConfig(),
datafile: config,
datafileOptions: {
autoUpdate: true,
updateInterval: 2 * 60 * 1000,
Expand Down Expand Up @@ -4201,7 +4202,7 @@ describe('lib/optimizely', function() {
describe('when the variation is missing the toggle', function() {
beforeEach(function() {
var experiment = optlyInstance.projectConfigManager.getConfig().experimentKeyMap.test_shared_feature;
var variation = fns.cloneDeep(experiment.variations[0]);
var variation = experiment.variations[0];
delete variation['featureEnabled'];
sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({
experiment: experiment,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
*/

var sprintf = require('@optimizely/js-sdk-utils').sprintf;
var lodashForOwn = require('lodash/forOwn');
var fns = require('../../utils/fns');

var ERROR_MESSAGES = require('../enums').ERROR_MESSAGES;
Expand All @@ -34,8 +33,8 @@ module.exports = {
*/
validate: function(attributes) {
if (typeof attributes === 'object' && !Array.isArray(attributes) && attributes !== null) {
lodashForOwn(attributes, function(value, key) {
if (typeof value === 'undefined') {
Object.keys(attributes).forEach(function(key) {
if (typeof attributes[key] === 'undefined') {
throw new Error(sprintf(ERROR_MESSAGES.UNDEFINED_ATTRIBUTE, MODULE_NAME, key));
}
});
Expand Down
1 change: 0 additions & 1 deletion packages/optimizely-sdk/lib/utils/fns/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ module.exports = {
return to;
}
},
cloneDeep: require('lodash/cloneDeep'),
currentTimestamp: function() {
return Math.round(new Date().getTime());
},
Expand Down
3 changes: 2 additions & 1 deletion packages/optimizely-sdk/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5cf5ddc

Please sign in to comment.