Skip to content

Commit 70c342a

Browse files
authored
feat (datafile management): Initialize datafile manager in Optimizely constructor, add onReady method (#13)
Summary: - Import and use datafile manager package. Create datafile manager instance in Optimizely constructor if `sdkKey`. is provided. - Provide `onReady` method, returning a promise based on the datafile manager's `onReady` method, or a fulfilled promise if no `sdkKey` was provided. - Update `isValidInstance` to distinguish between config validity and datafile/project config validity. - Move some validation/creation logic into a new function in the project config module: `tryCreatingProjectConfig` - Return early in top-level methods when `configObj` is not available Test plan: Unit tests Issues: https://optimizely.atlassian.net/browse/OASIS-4384
1 parent 4dcb5c4 commit 70c342a

File tree

9 files changed

+722
-71
lines changed

9 files changed

+722
-71
lines changed

packages/optimizely-sdk/lib/core/project_config/index.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ var fns = require('../../utils/fns');
1717
var enums = require('../../utils/enums');
1818
var sprintf = require('@optimizely/js-sdk-utils').sprintf;
1919
var stringValidator = require('../../utils/string_value_validator');
20+
var configValidator = require('../../utils/config_validator');
21+
var projectConfigSchema = require('./project_config_schema');
2022

2123
var EXPERIMENT_LAUNCHED_STATUS = 'Launched';
2224
var EXPERIMENT_RUNNING_STATUS = 'Running';
@@ -594,4 +596,36 @@ module.exports = {
594596
eventWithKeyExists: function(projectConfig, eventKey) {
595597
return projectConfig.eventKeyMap.hasOwnProperty(eventKey);
596598
},
599+
600+
/**
601+
* Try to create a project config object from the given datafile and
602+
* configuration properties.
603+
* If successful, return the project config object, otherwise return null.
604+
* @param {Object} config
605+
* @param {Object} config.datafile
606+
* @param {Object} config.errorHandler
607+
* @param {Object} config.jsonSchemaValidator
608+
* @param {Object} config.logger
609+
* @param {Object} config.skipJSONValidation
610+
* @return {Object|null} Project config object if datafile was valid, otherwise null
611+
*/
612+
tryCreatingProjectConfig: function(config) {
613+
var configObj = null;
614+
try {
615+
configValidator.validateDatafile(config.datafile);
616+
if (config.skipJSONValidation === true) {
617+
configObj = module.exports.createProjectConfig(config.datafile);
618+
config.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.SKIPPING_JSON_VALIDATION, MODULE_NAME));
619+
} else if (!config.jsonSchemaValidator) {
620+
configObj = module.exports.createProjectConfig(config.datafile);
621+
} else if (config.jsonSchemaValidator.validate(projectConfigSchema, config.datafile)) {
622+
configObj = module.exports.createProjectConfig(config.datafile);
623+
config.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.VALID_DATAFILE, MODULE_NAME));
624+
}
625+
} catch (ex) {
626+
config.logger.log(LOG_LEVEL.ERROR, ex.message);
627+
config.errorHandler.handleError(ex);
628+
}
629+
return configObj;
630+
}
597631
};

packages/optimizely-sdk/lib/core/project_config/index.tests.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
var projectConfig = require('./');
1717
var enums = require('../../utils/enums');
1818
var testDatafile = require('../../tests/test_data');
19+
var configValidator = require('../../utils/config_validator');
1920

2021
var _ = require('lodash/core');
2122
var fns = require('../../utils/fns');
@@ -727,4 +728,120 @@ describe('lib/core/project_config', function() {
727728
assert.strictEqual(didSetVariation, false);
728729
});
729730
});
731+
732+
describe('#tryCreatingProjectConfig', function() {
733+
var stubErrorHandler;
734+
var stubJsonSchemaValidator;
735+
var stubLogger;
736+
beforeEach(function() {
737+
stubErrorHandler = {
738+
handleError: sinon.stub(),
739+
};
740+
stubJsonSchemaValidator = {
741+
validate: sinon.stub().returns(true),
742+
};
743+
stubLogger = {
744+
log: sinon.stub(),
745+
};
746+
sinon.stub(projectConfig, 'createProjectConfig').returns({});
747+
sinon.stub(configValidator, 'validateDatafile').returns(true);
748+
});
749+
750+
afterEach(function() {
751+
projectConfig.createProjectConfig.restore();
752+
configValidator.validateDatafile.restore();
753+
});
754+
755+
it('returns a project config object created by createProjectConfig when all validation is applied and there are no errors', function() {
756+
configValidator.validateDatafile.returns(true);
757+
stubJsonSchemaValidator.validate.returns(true);
758+
var configObj = {
759+
foo: 'bar',
760+
experimentKeyMap: {
761+
a: { key: 'a' },
762+
b: { key: 'b' },
763+
}
764+
};
765+
projectConfig.createProjectConfig.returns(configObj);
766+
var result = projectConfig.tryCreatingProjectConfig({
767+
datafile: { foo: 'bar' },
768+
errorHandler: stubErrorHandler,
769+
jsonSchemaValidator: stubJsonSchemaValidator,
770+
logger: stubLogger,
771+
skipJSONValidation: false,
772+
});
773+
assert.deepEqual(result, configObj);
774+
});
775+
776+
it('returns null and calls handleError when validateDatafile throws', function() {
777+
configValidator.validateDatafile.throws();
778+
stubJsonSchemaValidator.validate.returns(true);
779+
var result = projectConfig.tryCreatingProjectConfig({
780+
datafile: { foo: 'bar' },
781+
errorHandler: stubErrorHandler,
782+
jsonSchemaValidator: stubJsonSchemaValidator,
783+
logger: stubLogger,
784+
skipJSONValidation: false,
785+
});
786+
assert.strictEqual(result, null);
787+
sinon.assert.calledOnce(stubErrorHandler.handleError);
788+
});
789+
790+
it('returns null and calls handleError when jsonSchemaValidator.validate throws', function() {
791+
configValidator.validateDatafile.returns(true);
792+
stubJsonSchemaValidator.validate.throws();
793+
var result = projectConfig.tryCreatingProjectConfig({
794+
datafile: { foo: 'bar' },
795+
errorHandler: stubErrorHandler,
796+
jsonSchemaValidator: stubJsonSchemaValidator,
797+
logger: stubLogger,
798+
skipJSONValidation: false,
799+
});
800+
assert.strictEqual(result, null);
801+
sinon.assert.calledOnce(stubErrorHandler.handleError);
802+
});
803+
804+
it('returns null when jsonSchemaValidator.validate returns false', function() {
805+
configValidator.validateDatafile.returns(true);
806+
stubJsonSchemaValidator.validate.returns(false);
807+
var result = projectConfig.tryCreatingProjectConfig({
808+
datafile: { foo: 'bar' },
809+
errorHandler: stubErrorHandler,
810+
jsonSchemaValidator: stubJsonSchemaValidator,
811+
logger: stubLogger,
812+
skipJSONValidation: false,
813+
});
814+
assert.strictEqual(result, null);
815+
});
816+
817+
it('does not call jsonSchemaValidator.validate when skipJSONValidation is true', function() {
818+
projectConfig.tryCreatingProjectConfig({
819+
datafile: { foo: 'bar' },
820+
errorHandler: stubErrorHandler,
821+
jsonSchemaValidator: stubJsonSchemaValidator,
822+
logger: stubLogger,
823+
skipJSONValidation: true,
824+
});
825+
sinon.assert.notCalled(stubJsonSchemaValidator.validate);
826+
});
827+
828+
it('skips json validation when jsonSchemaValidator is not provided', function() {
829+
configValidator.validateDatafile.returns(true);
830+
var configObj = {
831+
foo: 'bar',
832+
experimentKeyMap: {
833+
a: { key: 'a' },
834+
b: { key: 'b' },
835+
}
836+
};
837+
projectConfig.createProjectConfig.returns(configObj);
838+
var result = projectConfig.tryCreatingProjectConfig({
839+
datafile: { foo: 'bar' },
840+
errorHandler: stubErrorHandler,
841+
logger: stubLogger,
842+
});
843+
assert.deepEqual(result, configObj);
844+
sinon.assert.notCalled(stubErrorHandler.handleError);
845+
});
846+
});
730847
});

0 commit comments

Comments
 (0)