diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ff6d511..8dbe7e61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ All notable changes to the LaunchDarkly client-side JavaScript SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org). +## [1.7.1] - 2018-05-07 +### Fixed +- The client no longer creates an empty `XMLHttpRequest` at startup time (which could interfere with unit tests). +- The client now sends the SDK version to LaunchDarkly; previously it only sent the browser version. This will allow for better usage metrics in a future version of the LaunchDarkly UI. + ## [1.7.0] - 2018-04-27 ### Changed - The build now uses Rollup, Babel and Jest. diff --git a/package.json b/package.json index a5f54842..be3e2691 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ldclient-js", - "version": "1.7.0", + "version": "1.7.1", "description": "LaunchDarkly SDK for JavaScript", "author": "LaunchDarkly ", "license": "Apache-2.0", diff --git a/src/EventProcessor.js b/src/EventProcessor.js index 137678fb..149f27cc 100644 --- a/src/EventProcessor.js +++ b/src/EventProcessor.js @@ -1,17 +1,19 @@ import * as utils from './utils'; const MAX_URL_LENGTH = 2000; -const hasCors = 'withCredentials' in new XMLHttpRequest(); function sendEvents(eventsUrl, events, sync) { const src = eventsUrl + '?d=' + utils.base64URLEncode(JSON.stringify(events)); const send = onDone => { + const xhr = new XMLHttpRequest(); + const hasCors = 'withCredentials' in xhr; + // Detect browser support for CORS if (hasCors) { /* supports cross-domain requests */ - const xhr = new XMLHttpRequest(); xhr.open('GET', src, !sync); + utils.addLDHeaders(xhr); if (!sync) { xhr.addEventListener('load', onDone); diff --git a/src/Requestor.js b/src/Requestor.js index b68a92c6..ce2eb00a 100644 --- a/src/Requestor.js +++ b/src/Requestor.js @@ -25,9 +25,11 @@ function fetchJSON(endpoint, body, callback) { if (body) { xhr.open('REPORT', endpoint); xhr.setRequestHeader('Content-Type', 'application/json'); + utils.addLDHeaders(xhr); xhr.send(JSON.stringify(body)); } else { xhr.open('GET', endpoint); + utils.addLDHeaders(xhr); xhr.send(); } diff --git a/src/__tests__/EventProcessor-test.js b/src/__tests__/EventProcessor-test.js index 9440cbc7..6ce6457f 100644 --- a/src/__tests__/EventProcessor-test.js +++ b/src/__tests__/EventProcessor-test.js @@ -2,6 +2,7 @@ import sinon from 'sinon'; import EventSerializer from '../EventSerializer'; import EventProcessor from '../EventProcessor'; +import * as utils from '../utils'; describe('EventProcessor', () => { let sandbox; @@ -65,4 +66,15 @@ describe('EventProcessor', () => { expect(requests.length).toEqual(1); expect(requests[0].async).toEqual(false); }); + + it('should send custom user-agent header', () => { + const processor = EventProcessor('/fake-url', serializer); + const user = { key: 'foo' }; + const event = { kind: 'identify', key: user.key }; + processor.enqueue(event); + processor.flush(user, true); + + expect(requests.length).toEqual(1); + expect(requests[0].requestHeaders['X-LaunchDarkly-User-Agent']).toEqual(utils.getLDUserAgentString()); + }); }); diff --git a/src/__tests__/Requestor-test.js b/src/__tests__/Requestor-test.js index 406eb732..c2c0e30d 100644 --- a/src/__tests__/Requestor-test.js +++ b/src/__tests__/Requestor-test.js @@ -1,5 +1,6 @@ import sinon from 'sinon'; import Requestor from '../Requestor'; +import * as utils from '../utils'; describe('Requestor', () => { let server; @@ -83,4 +84,22 @@ describe('Requestor', () => { expect(handleFour.calledOnce).toEqual(true); expect(handleFive.calledOnce).toEqual(true); }); + + it('should send custom user-agent header in GET mode', () => { + const requestor = Requestor('http://requestee', 'FAKE_ENV', false); + const user = { key: 'foo' }; + requestor.fetchFlagSettings(user, 'hash1', sinon.spy()); + + expect(server.requests.length).toEqual(1); + expect(server.requests[0].requestHeaders['X-LaunchDarkly-User-Agent']).toEqual(utils.getLDUserAgentString()); + }); + + it('should send custom user-agent header in REPORT mode', () => { + const requestor = Requestor('http://requestee', 'FAKE_ENV', true); + const user = { key: 'foo' }; + requestor.fetchFlagSettings(user, 'hash1', sinon.spy()); + + expect(server.requests.length).toEqual(1); + expect(server.requests[0].requestHeaders['X-LaunchDarkly-User-Agent']).toEqual(utils.getLDUserAgentString()); + }); }); diff --git a/src/utils.js b/src/utils.js index 2b9f1024..7c8a97a1 100644 --- a/src/utils.js +++ b/src/utils.js @@ -126,3 +126,11 @@ export function chunkUserEventsForUrl(maxLength, events) { return allChunks; } + +export function getLDUserAgentString() { + return 'JSClient/' + window.VERSION; +} + +export function addLDHeaders(xhr) { + xhr.setRequestHeader('X-LaunchDarkly-User-Agent', getLDUserAgentString()); +}