diff --git a/event_processor.js b/event_processor.js index 89e8951..a67a4ac 100644 --- a/event_processor.js +++ b/event_processor.js @@ -1,5 +1,6 @@ const LRUCache = require('lrucache'); const request = require('request'); +const uuidv4 = require('uuid/v4'); const EventSummarizer = require('./event_summarizer'); const UserFilter = require('./user_filter'); const errors = require('./errors'); @@ -188,16 +189,16 @@ function EventProcessor(sdkKey, config, errorReporter) { config.logger.debug('Flushing %d events', worklist.length); - tryPostingEvents(worklist, resolve, reject, true); + tryPostingEvents(worklist, uuidv4(), resolve, reject, true); }), callback); }; - function tryPostingEvents(events, resolve, reject, canRetry) { + function tryPostingEvents(events, payloadId, resolve, reject, canRetry) { const retryOrReject = err => { if (canRetry) { config.logger && config.logger.warn('Will retry posting events after 1 second'); setTimeout(() => { - tryPostingEvents(events, resolve, reject, false); + tryPostingEvents(events, payloadId, resolve, reject, false); }, 1000); } else { reject(err); @@ -210,7 +211,8 @@ function EventProcessor(sdkKey, config, errorReporter) { headers: { 'Authorization': sdkKey, 'User-Agent': config.userAgent, - 'X-LaunchDarkly-Event-Schema': '3' + 'X-LaunchDarkly-Event-Schema': '3', + 'X-LaunchDarkly-Payload-ID': payloadId }, json: true, body: events, diff --git a/package-lock.json b/package-lock.json index 9e13c64..e8480ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7496,9 +7496,9 @@ } }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" }, "v8-compile-cache": { "version": "2.1.0", diff --git a/package.json b/package.json index a258891..1396c04 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "request-etag": "^2.0.3", "semver": "^6.3.0", "tunnel": "0.0.6", + "uuid": "^3.3.3", "winston": "2.4.1", "yaml": "1.0.1" }, diff --git a/test.js b/test.js deleted file mode 100644 index d5764f7..0000000 --- a/test.js +++ /dev/null @@ -1,32 +0,0 @@ -var ld = require('./index.js'); -var winston = require('winston'); - -var logger = new winston.Logger({ -level: 'debug', -transports: [ - new (winston.transports.Console)(({ - formatter: function(options) { - return '[LaunchDarkly] ' + (options.message ? options.message : ''); - } - })), -] -}); - -var fileSource = ld.FileDataSource({ paths: [ 'test.yml' ], autoUpdate: true, logger: logger }); - -var config = { - baseUri: 'https://ld-stg.launchdarkly.com', - streamUri: 'https://stream-stg.launchdarkly.com', - eventsUri: 'https://events-stg.launchdarkly.com', - sendEvents: false, - //updateProcessor: fileSource, - logger: logger -}; - -var client = ld.init('sdk-0acc1044-9cf7-40ea-919f-7b5a8540d9d8', config); - -client.on('ready', function() { - client.variation('catflag', {key: 'user'}, 'bear', function(err, value) { - console.log('catflag: ' + value); - }); -}); diff --git a/test/event_processor-test.js b/test/event_processor-test.js index 2745301..9e1f70d 100644 --- a/test/event_processor-test.js +++ b/test/event_processor-test.js @@ -483,6 +483,24 @@ describe('EventProcessor', () => { }); })); + it('sends unique payload IDs', eventsServerTest(async s => { + await withEventProcessor(defaultConfig, s, async ep => { + const e = { kind: 'identify', creationDate: 1000, user: user }; + ep.sendEvent(e); + await ep.flush(); + ep.sendEvent(e); + await ep.flush(); + + const req0 = await s.nextRequest(); + const req1 = await s.nextRequest(); + const id0 = req0.headers['x-launchdarkly-payload-id']; + const id1 = req1.headers['x-launchdarkly-payload-id']; + expect(id0).toBeTruthy(); + expect(id1).toBeTruthy(); + expect(id0).not.toEqual(id1); + }); + })); + function verifyUnrecoverableHttpError(status) { return eventsServerTest(async s => { s.forMethodAndPath('post', '/bulk', TestHttpHandlers.respond(status)); @@ -510,13 +528,18 @@ describe('EventProcessor', () => { await expect(ep.flush()).rejects.toThrow('error ' + status); expect(s.requestCount()).toEqual(2); - await s.nextRequest(); - await s.nextRequest(); + const req0 = await s.nextRequest(); + const req1 = await s.nextRequest(); + expect(req0.body).toEqual(req1.body); + const id0 = req0.headers['x-launchdarkly-payload-id']; + expect(req1.headers['x-launchdarkly-payload-id']).toEqual(id0); s.forMethodAndPath('post', '/bulk', TestHttpHandlers.respond(200)); ep.sendEvent(e); await ep.flush(); expect(s.requestCount()).toEqual(3); + const req2 = await s.nextRequest(); + expect(req2.headers['x-launchdarkly-payload-id']).not.toEqual(id0); }); }); }