diff --git a/packages/optimizely-sdk/lib/optimizely/index.js b/packages/optimizely-sdk/lib/optimizely/index.js index 15b03393c..7277a3d38 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.js +++ b/packages/optimizely-sdk/lib/optimizely/index.js @@ -362,7 +362,7 @@ Optimizely.prototype.getForcedVariation = function(experimentKey, userId) { Optimizely.prototype.__validateInputs = function(stringInputs, userAttributes, eventTags) { try { var inputKeys = Object.keys(stringInputs); - for (var index=0; index < inputKeys.length; index++) { + for (var index = 0; index < inputKeys.length; index++) { var key = inputKeys[index]; if (!stringValidator.validate(stringInputs[key])) { throw new Error(sprintf(ERROR_MESSAGES.INVALID_INPUT_FORMAT, MODULE_NAME, key)); @@ -449,12 +449,12 @@ Optimizely.prototype.__notActivatingExperiment = function(experimentKey, userId) Optimizely.prototype.__dispatchEvent = function (eventToDispatch, callback) { var eventDispatcherResponse = this.eventDispatcher.dispatchEvent(eventToDispatch, callback); //checking that response value is a promise, not a request object - if (typeof eventDispatcherResponse == "object" && !eventDispatcherResponse.hasOwnProperty('uri')) { + if (!fns.isEmpty(eventDispatcherResponse) && typeof eventDispatcherResponse.then === 'function') { eventDispatcherResponse.then(function() { callback(); }); } -} +}; /** * Filters out attributes/eventTags with null or undefined values @@ -464,11 +464,11 @@ Optimizely.prototype.__dispatchEvent = function (eventToDispatch, callback) { Optimizely.prototype.__filterEmptyValues = function (map) { for (var key in map) { if (map.hasOwnProperty(key) && (map[key] === null || map[key] === undefined)) { - delete map[key] + delete map[key]; } } return map; -} +}; /** * Returns true if the feature is enabled for the given user. diff --git a/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.js b/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.js index 49bcdbb30..4d8b2881f 100644 --- a/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.js +++ b/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.js @@ -1,5 +1,5 @@ /** - * Copyright 2016-2017, Optimizely + * Copyright 2016-2018, Optimizely * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,35 +13,54 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -var request = require('request'); - -var POST_HEADERS = {'content-type': 'application/json'}; +var http = require('http'); +var https = require('https'); +var url = require('url'); module.exports = { /** * Dispatch an HTTP request to the given url and the specified options * @param {Object} eventObj Event object containing * @param {string} eventObj.url the url to make the request to - * @param {Object} eventObj.params parameters to pass to the request - * @param {string} eventObj.httpVerb the HTTP request method type + * @param {Object} eventObj.params parameters to pass to the request (i.e. in the POST body) + * @param {string} eventObj.httpVerb the HTTP request method type. only POST is supported. * @param {function} callback callback to execute - * @return {Promise} the payload from the request + * @return {ClientRequest|undefined} ClientRequest object which made the request, or undefined if no request was made (error) */ dispatchEvent: function(eventObj, callback) { + // Non-POST requests not supported + if (eventObj.httpVerb !== 'POST') { + callback(new Error('httpVerb not supported: ' + eventObj.httpVerb)); + return; + } + + var parsedUrl = url.parse(eventObj.url); + var path = parsedUrl.path; + if (parsedUrl.query) { + path += '?' + parsedUrl.query; + } + + var dataString = JSON.stringify(eventObj.params); + var requestOptions = { - uri: eventObj.url, - body: eventObj.params, - headers: POST_HEADERS, - method: eventObj.httpVerb, - json: true, + host: parsedUrl.host, + path: parsedUrl.path, + method: 'POST', + headers: { + 'content-type': 'application/json', + 'content-length': dataString.length.toString(), + } }; var requestCallback = function(error, response, body) { if (response && response.statusCode && response.statusCode >= 200 && response.statusCode < 400 && callback && typeof callback == 'function') { callback(); } - } + }; - return request(requestOptions, requestCallback); + var req = (parsedUrl.protocol === 'http:' ? http : https).request(requestOptions, requestCallback); + req.write(dataString); + req.end(); + return req; } }; diff --git a/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.tests.js b/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.tests.js index 84f0c90dc..1c4907b9c 100644 --- a/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.tests.js +++ b/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.tests.js @@ -1,5 +1,5 @@ /** - * Copyright 2016-2017, Optimizely + * Copyright 2016-2018, Optimizely * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ describe('lib/plugins/event_dispatcher/node', function() { describe('dispatchEvent', function() { var stubCallback = { callback: function() {} - } + }; beforeEach(function() { sinon.stub(stubCallback, 'callback'); @@ -43,7 +43,7 @@ describe('lib/plugins/event_dispatcher/node', function() { it('should send a POST request with the specified params', function(done) { var eventObj = { url: 'https://cdn.com/event', - body: { + params: { id: 123, }, httpVerb: 'POST', @@ -55,14 +55,14 @@ describe('lib/plugins/event_dispatcher/node', function() { done(); }) .on('error', function(error) { - assert.fail('status code okay', 'status code not okay', "") - }) + assert.fail('status code okay', 'status code not okay', ''); + }); }); it('should execute the callback passed to event dispatcher', function(done) { var eventObj = { url: 'https://cdn.com/event', - body: { + params: { id: 123, }, httpVerb: 'POST', @@ -74,10 +74,29 @@ describe('lib/plugins/event_dispatcher/node', function() { sinon.assert.calledOnce(stubCallback.callback); }) .on('error', function(error) { - assert.fail('status code okay', 'status code not okay', "") - }) + assert.fail('status code okay', 'status code not okay', ''); + }); }); + it('rejects GET httpVerb', function(done) { + var eventObj = { + url: 'https://cdn.com/event', + params: { + id: 123, + }, + httpVerb: 'GET', + }; + + var callback = function(err) { + if (err) { + done(); + } else { + done(new Error('expected error not thrown')); + } + }; + + eventDispatcher.dispatchEvent(eventObj, callback); + }); }); }); }); diff --git a/packages/optimizely-sdk/package.json b/packages/optimizely-sdk/package.json index b702bc0ed..c44ea3b91 100644 --- a/packages/optimizely-sdk/package.json +++ b/packages/optimizely-sdk/package.json @@ -35,7 +35,6 @@ "json-schema": "^0.2.3", "lodash": "^4.0.0", "murmurhash": "0.0.2", - "request": "~2.83.0", "sprintf": "^0.1.5", "uuid": "^3.0.1" }, diff --git a/packages/optimizely-sdk/srcclr.yml b/packages/optimizely-sdk/srcclr.yml new file mode 100644 index 000000000..5941418eb --- /dev/null +++ b/packages/optimizely-sdk/srcclr.yml @@ -0,0 +1 @@ +scope: production