Skip to content

Commit

Permalink
prepare 3.4.0 release (#52)
Browse files Browse the repository at this point in the history
  • Loading branch information
LaunchDarklyReleaseBot authored Oct 15, 2021
1 parent a5a5ff6 commit 585d01a
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 15 deletions.
12 changes: 0 additions & 12 deletions docs/typedoc.js

This file was deleted.

2 changes: 1 addition & 1 deletion src/EventSender.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default function EventSender(platform, environmentId, options) {
'X-LaunchDarkly-Payload-ID': payloadId,
});
return platform
.httpRequest('POST', url, headers, jsonBody)
.httpRequest('POST', url, utils.transformHeaders(headers, options), jsonBody)
.promise.then(result => {
if (!result) {
// This was a response from a fire-and-forget request, so we won't have a status.
Expand Down
2 changes: 1 addition & 1 deletion src/Requestor.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function Requestor(platform, options, environment) {
activeRequests[endpoint] = coalescer;
}

const req = platform.httpRequest(method, endpoint, headers, body);
const req = platform.httpRequest(method, endpoint, utils.transformHeaders(headers, options), body);
const p = req.promise.then(
result => {
if (result.status === 200) {
Expand Down
3 changes: 2 additions & 1 deletion src/Stream.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as messages from './messages';
import { base64URLEncode, getLDHeaders, objectHasOwnProperty } from './utils';
import { base64URLEncode, getLDHeaders, transformHeaders, objectHasOwnProperty } from './utils';

// The underlying event source implementation is abstracted via the platform object, which should
// have these three properties:
Expand Down Expand Up @@ -104,6 +104,7 @@ export default function Stream(platform, config, environment, diagnosticsAccumul
} else {
url = evalUrlPrefix + '/' + base64URLEncode(JSON.stringify(user));
}
options.headers = transformHeaders(options.headers, config);
if (withReasons) {
query = query + (query ? '&' : '') + 'withReasons=true';
}
Expand Down
17 changes: 17 additions & 0 deletions src/__tests__/EventSender-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,23 @@ describe('EventSender', () => {
expect(r.headers['x-launchdarkly-wrapper']).toEqual('FakeSDK');
});

it('should send transformed headers if requestHeaderTransform function is provided', async () => {
const headerTransform = input => {
const output = { ...input };
output['c'] = '30';
return output;
};
const options = { requestHeaderTransform: headerTransform };
const server = platform.testing.http.newServer();
server.byDefault(respond(202));
const sender = EventSender(platform, envId, options);
const event = { kind: 'identify', key: 'userKey' };
await sender.sendEvents([event], server.url);

const r = await server.nextRequest();
expect(r.headers['c']).toEqual('30');
});

describe('retry on recoverable HTTP error', () => {
const retryableStatuses = [400, 408, 429, 500, 503];
for (const i in retryableStatuses) {
Expand Down
18 changes: 18 additions & 0 deletions src/__tests__/Requestor-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,24 @@ describe('Requestor', () => {
});
});

it('sends transformed headers if requestHeaderTransform function is provided', async () => {
await withServer(async (baseConfig, server) => {
const headerTransform = input => {
const output = { ...input };
output['b'] = '20';
return output;
};
const config = { ...baseConfig, requestHeaderTransform: headerTransform };
const requestor = Requestor(platform, config, env);

await requestor.fetchFlagSettings(user);

expect(server.requests.length()).toEqual(1);
const req = await server.requests.take();
expect(req.headers['b']).toEqual('20');
});
});

it('returns parsed JSON response on success', async () => {
const data = { foo: 'bar' };
await withServer(async (baseConfig, server) => {
Expand Down
14 changes: 14 additions & 0 deletions src/__tests__/Stream-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,20 @@ describe('Stream', () => {
expect(created.options.headers).toEqual({});
});

it('sends transformed headers if requestHeaderTransform function is provided', async () => {
const headerTransform = input => {
const output = { ...input };
output['a'] = '10';
return output;
};
const config = { ...defaultConfig, requestHeaderTransform: headerTransform };
const stream = new Stream(platform, config, envName);
stream.connect(user, null, {});

const created = await platform.testing.expectStream(makeExpectedStreamUrl(encodedUser));
expect(created.options.headers).toEqual({ ...baseHeaders, a: '10' });
});

it('sets event listeners', async () => {
const stream = new Stream(platform, defaultConfig, envName);
const fn1 = jest.fn();
Expand Down
33 changes: 33 additions & 0 deletions src/__tests__/utils-test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
base64URLEncode,
getLDHeaders,
transformHeaders,
getLDUserAgentString,
wrapPromiseCallback,
chunkUserEventsForUrl,
Expand Down Expand Up @@ -79,6 +80,38 @@ describe('utils', () => {
});
});

describe('transformHeaders', () => {
it('does not modify the headers if the option is not available', () => {
const inputHeaders = { a: '1', b: '2' };
const headers = transformHeaders(inputHeaders, {});
expect(headers).toEqual(inputHeaders);
});

it('modifies the headers if the option has a transform', () => {
const inputHeaders = { c: '3', d: '4' };
const outputHeaders = { c: '9', d: '4', e: '5' };
const headerTransform = input => {
const output = { ...input };
output['c'] = '9';
output['e'] = '5';
return output;
};
const headers = transformHeaders(inputHeaders, { requestHeaderTransform: headerTransform });
expect(headers).toEqual(outputHeaders);
});

it('cannot mutate the input header object', () => {
const inputHeaders = { f: '6' };
const expectedInputHeaders = { f: '6' };
const headerMutate = input => {
input['f'] = '7'; // eslint-disable-line no-param-reassign
return input;
};
transformHeaders(inputHeaders, { requestHeaderTransform: headerMutate });
expect(inputHeaders).toEqual(expectedInputHeaders);
});
});

describe('getLDUserAgentString', () => {
it('uses platform user-agent and package version by default', () => {
const platform = stubPlatform.defaults();
Expand Down
1 change: 1 addition & 0 deletions src/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const baseOptionDefs = {
sendEvents: { default: true },
streaming: { type: 'boolean' }, // default for this is undefined, which is different from false
sendLDHeaders: { default: true },
requestHeaderTransform: { type: 'function' },
inlineUsersInEvents: { default: false },
allowFrequentDuplicateEvents: { default: false },
sendEventsOnlyForVariation: { default: false },
Expand Down
7 changes: 7 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ export function getLDHeaders(platform, options) {
return h;
}

export function transformHeaders(headers, options) {
if (!options || !options.requestHeaderTransform) {
return headers;
}
return options.requestHeaderTransform({ ...headers });
}

export function extend(...objects) {
return objects.reduce((acc, obj) => ({ ...acc, ...obj }), {});
}
Expand Down
1 change: 1 addition & 0 deletions test-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ var allBaseOptions: ld.LDOptionsBase = {
streaming: true,
useReport: true,
sendLDHeaders: true,
requestHeaderTransform: (x) => x,
evaluationReasons: true,
sendEvents: true,
allAttributesPrivate: true,
Expand Down
8 changes: 8 additions & 0 deletions typings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ declare module 'launchdarkly-js-sdk-common' {
*/
sendLDHeaders?: boolean;

/**
* A transform function for dynamic configuration of HTTP headers.
*
* This method will run last in the header generation sequence, so the function should have
* all system generated headers in case those also need to be modified.
*/
requestHeaderTransform?: (headers: Map<string, string>) => Map<string, string>;

/**
* Whether LaunchDarkly should provide additional information about how flag values were
* calculated.
Expand Down

0 comments on commit 585d01a

Please sign in to comment.