From b9f366d51b04801ed1a64352bb3243a08202e182 Mon Sep 17 00:00:00 2001 From: Clay Embry Date: Wed, 12 Aug 2020 17:17:59 -0500 Subject: [PATCH 1/3] Fixed asyncWithLDProvider.tsx and provider.tsx so that they only provide flag changes for subscribed flags. --- src/asyncWithLDProvider.test.tsx | 15 ++++++++ src/asyncWithLDProvider.tsx | 12 +++---- src/provider.test.tsx | 27 +++++++++++++++ src/provider.tsx | 13 +++---- src/utils.test.ts | 59 +++++++++++++++++++++++++++++++- src/utils.ts | 28 +++++++++++++-- 6 files changed, 135 insertions(+), 19 deletions(-) diff --git a/src/asyncWithLDProvider.test.tsx b/src/asyncWithLDProvider.test.tsx index 2d08746..8e0d61a 100644 --- a/src/asyncWithLDProvider.test.tsx +++ b/src/asyncWithLDProvider.test.tsx @@ -179,4 +179,19 @@ describe('asyncWithLDProvider', () => { expect(mockInitLDClient).toHaveBeenCalledWith(clientSideID, user, defaultReactOptions, options, flags); expect(receivedNode).toHaveTextContent('{"devTestFlag":true,"launchDoggly":true}'); }); + + test('only updates to subscribed flags are pushed to the Provider', async () => { + mockInitLDClient.mockImplementation(() => ({ + flags: { testFlag: true }, + ldClient: mockLDClient, + })); + mockLDClient.on.mockImplementation((e: string, cb: (c: LDFlagChangeset) => void) => { + cb({ 'test-flag': { current: false, previous: true }, 'another-test-flag': { current: false, previous: true } }); + }); + const options: LDOptions = {}; + const subscribedFlags = { 'test-flag': false }; + const receivedNode = await renderWithConfig({ clientSideID, user, options, flags: subscribedFlags }); + + expect(receivedNode).toHaveTextContent('{"testFlag":false}'); + }); }); diff --git a/src/asyncWithLDProvider.tsx b/src/asyncWithLDProvider.tsx index 8b35a61..8930490 100644 --- a/src/asyncWithLDProvider.tsx +++ b/src/asyncWithLDProvider.tsx @@ -4,7 +4,7 @@ import { LDFlagSet, LDFlagChangeset } from 'launchdarkly-js-client-sdk'; import { defaultReactOptions, ProviderConfig } from './types'; import { Provider } from './context'; import initLDClient from './initLDClient'; -import { camelCaseKeys } from './utils'; +import {camelCaseKeys, getFlattenedFlagsFromChangeset} from './utils'; /** * This is an async function which initializes LaunchDarkly's JS SDK (`launchdarkly-js-client-sdk`) @@ -46,14 +46,10 @@ export default async function asyncWithLDProvider(config: ProviderConfig) { } ldClient.on('change', (changes: LDFlagChangeset) => { - const flattened: LDFlagSet = {}; - for (const key in changes) { - // tslint:disable-next-line:no-unsafe-any - const flagKey = reactOptions.useCamelCaseFlagKeys ? camelCase(key) : key; - flattened[flagKey] = changes[key].current; + const flattened: LDFlagSet | null = getFlattenedFlagsFromChangeset(changes, flags, reactOptions); + if (flattened) { + setLDData(prev => ({ ...prev, flags: { ...prev.flags, ...flattened } })); } - - setLDData(prev => ({ ...prev, flags: { ...prev.flags, ...flattened } })); }); }, []); diff --git a/src/provider.test.tsx b/src/provider.test.tsx index 7de1762..d6637f2 100644 --- a/src/provider.test.tsx +++ b/src/provider.test.tsx @@ -311,4 +311,31 @@ describe('LDProvider', () => { expect(mockInitLDClient).toHaveBeenCalledWith(clientSideID, user, defaultReactOptions, options, undefined); }); + + test('only updates to subscribed flags are pushed to the Provider', async () => { + mockInitLDClient.mockImplementation(() => ({ + flags: { testFlag: true }, + ldClient: mockLDClient, + })); + mockLDClient.on.mockImplementation((e: string, cb: (c: LDFlagChangeset) => void) => { + cb({ 'test-flag': { current: false, previous: true }, 'another-test-flag': { current: false, previous: true } }); + }); + const options: LDOptions = {}; + const user: LDUser = { key: 'yus', name: 'yus ng' }; + const subscribedFlags = { 'test-flag': false }; + const props: ProviderConfig = { clientSideID, user, options, flags: subscribedFlags }; + const LaunchDarklyApp = ( + + + + ); + const instance = create(LaunchDarklyApp).root.findByType(LDProvider).instance as EnhancedComponent; + const mockSetState = jest.spyOn(instance, 'setState'); + + await instance.componentDidMount(); + const callback = mockSetState.mock.calls[1][0] as (flags: LDFlagSet) => LDFlagSet; + const newState = callback({}); + + expect(newState).toEqual({ flags: { 'testFlag': false } }); + }); }); diff --git a/src/provider.tsx b/src/provider.tsx index b4e8717..1bb52e7 100644 --- a/src/provider.tsx +++ b/src/provider.tsx @@ -4,7 +4,7 @@ import { LDClient, LDFlagSet, LDFlagChangeset } from 'launchdarkly-js-client-sdk import { EnhancedComponent, ProviderConfig, defaultReactOptions } from './types'; import { Provider, LDContext as HocState } from './context'; import initLDClient from './initLDClient'; -import { camelCaseKeys } from './utils'; +import {camelCaseKeys, getFlattenedFlagsFromChangeset} from './utils'; /** * The `LDProvider` is a component which accepts a config object which is used to @@ -52,15 +52,12 @@ class LDProvider extends React.Component implements En getReactOptions = () => ({ ...defaultReactOptions, ...this.props.reactOptions }); subscribeToChanges = (ldClient: LDClient) => { + const { flags } = this.props; ldClient.on('change', (changes: LDFlagChangeset) => { - const flattened: LDFlagSet = {}; - for (const key in changes) { - // tslint:disable-next-line:no-unsafe-any - const { useCamelCaseFlagKeys } = this.getReactOptions(); - const flagKey = useCamelCaseFlagKeys ? camelCase(key) : key; - flattened[flagKey] = changes[key].current; + const flattened: LDFlagSet | null = getFlattenedFlagsFromChangeset(changes, flags, this.getReactOptions()); + if (flattened) { + this.setState(({flags}) => ({flags: {...flags, ...flattened}})); } - this.setState(({ flags }) => ({ flags: { ...flags, ...flattened } })); }); }; diff --git a/src/utils.test.ts b/src/utils.test.ts index db45c4c..44d72f3 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -1,4 +1,6 @@ -import { camelCaseKeys } from './utils'; +import {camelCaseKeys, getFlattenedFlagsFromChangeset} from './utils'; +import {LDFlagChangeset, LDFlagSet} from 'launchdarkly-js-client-sdk'; +import {LDReactOptions} from './types'; describe('Utils', () => { test('camelCaseKeys should ignore system keys', () => { @@ -15,4 +17,59 @@ describe('Utils', () => { const result = camelCaseKeys(bootstrap); expect(result).toEqual({ testFlag: true, anotherTestFlag: false }); }); + + test('getFlattenedFlagsFromChangeset should return current values of all flags when no targetFlags specified', () => { + const targetFlags: LDFlagSet | undefined = undefined; + const flagChanges: LDFlagChangeset = { + 'test-flag': {current: true, previous: false}, + 'another-test-flag': {current: false, previous: true} + }; + const reactOptions: LDReactOptions = { + useCamelCaseFlagKeys: true, + }; + const flattened = getFlattenedFlagsFromChangeset(flagChanges, targetFlags, reactOptions); + + expect(flattened).toEqual({anotherTestFlag: false, testFlag: true}); + }) + + test('getFlattenedFlagsFromChangeset should return current values only of targetFlags when targetFlags specified', () => { + const targetFlags: LDFlagSet | undefined = {'test-flag': false}; + const flagChanges: LDFlagChangeset = { + 'test-flag': {current: true, previous: false}, + 'another-test-flag': {current: false, previous: true} + }; + const reactOptions: LDReactOptions = { + useCamelCaseFlagKeys: true, + }; + const flattened = getFlattenedFlagsFromChangeset(flagChanges, targetFlags, reactOptions); + + expect(flattened).toEqual({testFlag: true}); + }) + + test('getFlattenedFlagsFromChangeset should return null when no targetFlags are changed ', () => { + const targetFlags: LDFlagSet | undefined = {'test-flag': false}; + const flagChanges: LDFlagChangeset = { + 'another-test-flag': {current: false, previous: true} + }; + const reactOptions: LDReactOptions = { + useCamelCaseFlagKeys: true, + }; + const flattened = getFlattenedFlagsFromChangeset(flagChanges, targetFlags, reactOptions); + + expect(flattened).toBeNull; + }) + + test('getFlattenedFlagsFromChangeset should not change flags to camelCase when reactOptions.useCamelCaseFlagKeys is false ', () => { + const targetFlags: LDFlagSet | undefined = undefined; + const flagChanges: LDFlagChangeset = { + 'test-flag': {current: true, previous: false}, + 'another-test-flag': {current: false, previous: true} + }; + const reactOptions: LDReactOptions = { + useCamelCaseFlagKeys: false, + }; + const flattened = getFlattenedFlagsFromChangeset(flagChanges, targetFlags, reactOptions); + + expect(flattened).toEqual({'another-test-flag': false, 'test-flag': true}); + }) }); diff --git a/src/utils.ts b/src/utils.ts index 88e2eec..92103f1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,6 @@ -import { LDFlagSet } from 'launchdarkly-js-client-sdk'; +import {LDFlagChangeset, LDFlagSet} from 'launchdarkly-js-client-sdk'; import camelCase from 'lodash.camelcase'; +import {LDReactOptions} from "./types"; /** * Transforms a set of flags so that their keys are camelCased. This function ignores @@ -20,4 +21,27 @@ export const camelCaseKeys = (rawFlags: LDFlagSet) => { return flags; }; -export default { camelCaseKeys }; +/** + * Gets the flags to pass to the provider from the changeset. + * + * @param changes the `LDFlagChangeset` from the ldClient onchange handler. + * @param targetFlags if targetFlags are specified, changes to other flags are ignored and not returned in the + * flattened `LDFlagSet` + * @param reactOptions reactOptions.useCamelCaseFlagKeys is used to determine whether to + * @return an `LDFlagSet` with the current flag values from the LDFlagChangeset filtered by `targetFlags` + * or null if none of the targetFlags were changed + */ +export const getFlattenedFlagsFromChangeset = (changes: LDFlagChangeset, targetFlags: LDFlagSet | undefined, + reactOptions: LDReactOptions): LDFlagSet | null => { + const flattened: LDFlagSet = {}; + for (const key in changes) { + if (targetFlags === undefined || targetFlags[key] !== undefined) { + // tslint:disable-next-line:no-unsafe-any + const flagKey = reactOptions.useCamelCaseFlagKeys ? camelCase(key) : key; + flattened[flagKey] = changes[key].current; + } + } + return Object.keys(flattened).length > 0 ? flattened : null; +} + +export default { camelCaseKeys, getFlattenedFlagsFromChangeset }; From 9595745097de6431f16500a3655d7b01ebf8ca14 Mon Sep 17 00:00:00 2001 From: Clay Embry Date: Wed, 12 Aug 2020 17:27:37 -0500 Subject: [PATCH 2/3] Removed unused imports. --- src/asyncWithLDProvider.tsx | 1 - src/provider.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/asyncWithLDProvider.tsx b/src/asyncWithLDProvider.tsx index 8930490..2abb8c5 100644 --- a/src/asyncWithLDProvider.tsx +++ b/src/asyncWithLDProvider.tsx @@ -1,5 +1,4 @@ import React, { useState, useEffect, FunctionComponent } from 'react'; -import camelCase from 'lodash.camelcase'; import { LDFlagSet, LDFlagChangeset } from 'launchdarkly-js-client-sdk'; import { defaultReactOptions, ProviderConfig } from './types'; import { Provider } from './context'; diff --git a/src/provider.tsx b/src/provider.tsx index 1bb52e7..27ea1b8 100644 --- a/src/provider.tsx +++ b/src/provider.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import camelCase from 'lodash.camelcase'; import { LDClient, LDFlagSet, LDFlagChangeset } from 'launchdarkly-js-client-sdk'; import { EnhancedComponent, ProviderConfig, defaultReactOptions } from './types'; import { Provider, LDContext as HocState } from './context'; From 976ceff8c8a4ef857d2e5994d32d6a3f190839a3 Mon Sep 17 00:00:00 2001 From: Clay Embry Date: Thu, 13 Aug 2020 10:37:15 -0500 Subject: [PATCH 3/3] Addressed PR feedback. Fixed linting errors. --- src/asyncWithLDProvider.test.tsx | 8 +++--- src/asyncWithLDProvider.tsx | 6 ++--- src/provider.test.tsx | 14 +++++----- src/provider.tsx | 10 ++++---- src/utils.test.ts | 44 ++++++++++++++++---------------- src/utils.ts | 22 +++++++++------- 6 files changed, 54 insertions(+), 50 deletions(-) diff --git a/src/asyncWithLDProvider.test.tsx b/src/asyncWithLDProvider.test.tsx index 8e0d61a..f9796d2 100644 --- a/src/asyncWithLDProvider.test.tsx +++ b/src/asyncWithLDProvider.test.tsx @@ -182,16 +182,16 @@ describe('asyncWithLDProvider', () => { test('only updates to subscribed flags are pushed to the Provider', async () => { mockInitLDClient.mockImplementation(() => ({ - flags: { testFlag: true }, + flags: { testFlag: 2 }, ldClient: mockLDClient, })); mockLDClient.on.mockImplementation((e: string, cb: (c: LDFlagChangeset) => void) => { - cb({ 'test-flag': { current: false, previous: true }, 'another-test-flag': { current: false, previous: true } }); + cb({ 'test-flag': { current: 3, previous: 2 }, 'another-test-flag': { current: false, previous: true } }); }); const options: LDOptions = {}; - const subscribedFlags = { 'test-flag': false }; + const subscribedFlags = { 'test-flag': 1 }; const receivedNode = await renderWithConfig({ clientSideID, user, options, flags: subscribedFlags }); - expect(receivedNode).toHaveTextContent('{"testFlag":false}'); + expect(receivedNode).toHaveTextContent('{"testFlag":3}'); }); }); diff --git a/src/asyncWithLDProvider.tsx b/src/asyncWithLDProvider.tsx index 2abb8c5..688d56e 100644 --- a/src/asyncWithLDProvider.tsx +++ b/src/asyncWithLDProvider.tsx @@ -3,7 +3,7 @@ import { LDFlagSet, LDFlagChangeset } from 'launchdarkly-js-client-sdk'; import { defaultReactOptions, ProviderConfig } from './types'; import { Provider } from './context'; import initLDClient from './initLDClient'; -import {camelCaseKeys, getFlattenedFlagsFromChangeset} from './utils'; +import { camelCaseKeys, getFlattenedFlagsFromChangeset } from './utils'; /** * This is an async function which initializes LaunchDarkly's JS SDK (`launchdarkly-js-client-sdk`) @@ -45,8 +45,8 @@ export default async function asyncWithLDProvider(config: ProviderConfig) { } ldClient.on('change', (changes: LDFlagChangeset) => { - const flattened: LDFlagSet | null = getFlattenedFlagsFromChangeset(changes, flags, reactOptions); - if (flattened) { + const flattened: LDFlagSet = getFlattenedFlagsFromChangeset(changes, flags, reactOptions); + if (Object.keys(flattened).length > 0) { setLDData(prev => ({ ...prev, flags: { ...prev.flags, ...flattened } })); } }); diff --git a/src/provider.test.tsx b/src/provider.test.tsx index d6637f2..4bc7e49 100644 --- a/src/provider.test.tsx +++ b/src/provider.test.tsx @@ -314,20 +314,20 @@ describe('LDProvider', () => { test('only updates to subscribed flags are pushed to the Provider', async () => { mockInitLDClient.mockImplementation(() => ({ - flags: { testFlag: true }, + flags: { testFlag: 2 }, ldClient: mockLDClient, })); mockLDClient.on.mockImplementation((e: string, cb: (c: LDFlagChangeset) => void) => { - cb({ 'test-flag': { current: false, previous: true }, 'another-test-flag': { current: false, previous: true } }); + cb({ 'test-flag': { current: 3, previous: 2 }, 'another-test-flag': { current: false, previous: true } }); }); const options: LDOptions = {}; const user: LDUser = { key: 'yus', name: 'yus ng' }; - const subscribedFlags = { 'test-flag': false }; + const subscribedFlags = { 'test-flag': 1 }; const props: ProviderConfig = { clientSideID, user, options, flags: subscribedFlags }; const LaunchDarklyApp = ( - - - + + + ); const instance = create(LaunchDarklyApp).root.findByType(LDProvider).instance as EnhancedComponent; const mockSetState = jest.spyOn(instance, 'setState'); @@ -336,6 +336,6 @@ describe('LDProvider', () => { const callback = mockSetState.mock.calls[1][0] as (flags: LDFlagSet) => LDFlagSet; const newState = callback({}); - expect(newState).toEqual({ flags: { 'testFlag': false } }); + expect(newState).toEqual({ flags: { testFlag: 3 } }); }); }); diff --git a/src/provider.tsx b/src/provider.tsx index 27ea1b8..cceddb9 100644 --- a/src/provider.tsx +++ b/src/provider.tsx @@ -3,7 +3,7 @@ import { LDClient, LDFlagSet, LDFlagChangeset } from 'launchdarkly-js-client-sdk import { EnhancedComponent, ProviderConfig, defaultReactOptions } from './types'; import { Provider, LDContext as HocState } from './context'; import initLDClient from './initLDClient'; -import {camelCaseKeys, getFlattenedFlagsFromChangeset} from './utils'; +import { camelCaseKeys, getFlattenedFlagsFromChangeset } from './utils'; /** * The `LDProvider` is a component which accepts a config object which is used to @@ -51,11 +51,11 @@ class LDProvider extends React.Component implements En getReactOptions = () => ({ ...defaultReactOptions, ...this.props.reactOptions }); subscribeToChanges = (ldClient: LDClient) => { - const { flags } = this.props; + const { flags: targetFlags } = this.props; ldClient.on('change', (changes: LDFlagChangeset) => { - const flattened: LDFlagSet | null = getFlattenedFlagsFromChangeset(changes, flags, this.getReactOptions()); - if (flattened) { - this.setState(({flags}) => ({flags: {...flags, ...flattened}})); + const flattened: LDFlagSet = getFlattenedFlagsFromChangeset(changes, targetFlags, this.getReactOptions()); + if (Object.keys(flattened).length > 0) { + this.setState(({ flags }) => ({ flags: { ...flags, ...flattened } })); } }); }; diff --git a/src/utils.test.ts b/src/utils.test.ts index 44d72f3..02bc348 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -1,6 +1,6 @@ -import {camelCaseKeys, getFlattenedFlagsFromChangeset} from './utils'; -import {LDFlagChangeset, LDFlagSet} from 'launchdarkly-js-client-sdk'; -import {LDReactOptions} from './types'; +import { camelCaseKeys, getFlattenedFlagsFromChangeset } from './utils'; +import { LDFlagChangeset, LDFlagSet } from 'launchdarkly-js-client-sdk'; +import { LDReactOptions } from './types'; describe('Utils', () => { test('camelCaseKeys should ignore system keys', () => { @@ -21,55 +21,55 @@ describe('Utils', () => { test('getFlattenedFlagsFromChangeset should return current values of all flags when no targetFlags specified', () => { const targetFlags: LDFlagSet | undefined = undefined; const flagChanges: LDFlagChangeset = { - 'test-flag': {current: true, previous: false}, - 'another-test-flag': {current: false, previous: true} + 'test-flag': { current: true, previous: false }, + 'another-test-flag': { current: false, previous: true }, }; const reactOptions: LDReactOptions = { useCamelCaseFlagKeys: true, }; const flattened = getFlattenedFlagsFromChangeset(flagChanges, targetFlags, reactOptions); - expect(flattened).toEqual({anotherTestFlag: false, testFlag: true}); - }) + expect(flattened).toEqual({ anotherTestFlag: false, testFlag: true }); + }); - test('getFlattenedFlagsFromChangeset should return current values only of targetFlags when targetFlags specified', () => { - const targetFlags: LDFlagSet | undefined = {'test-flag': false}; + test('getFlattenedFlagsFromChangeset should return current values only of targetFlags when specified', () => { + const targetFlags: LDFlagSet | undefined = { 'test-flag': false }; const flagChanges: LDFlagChangeset = { - 'test-flag': {current: true, previous: false}, - 'another-test-flag': {current: false, previous: true} + 'test-flag': { current: true, previous: false }, + 'another-test-flag': { current: false, previous: true }, }; const reactOptions: LDReactOptions = { useCamelCaseFlagKeys: true, }; const flattened = getFlattenedFlagsFromChangeset(flagChanges, targetFlags, reactOptions); - expect(flattened).toEqual({testFlag: true}); - }) + expect(flattened).toEqual({ testFlag: true }); + }); - test('getFlattenedFlagsFromChangeset should return null when no targetFlags are changed ', () => { - const targetFlags: LDFlagSet | undefined = {'test-flag': false}; + test('getFlattenedFlagsFromChangeset should return empty LDFlagSet when no targetFlags are changed ', () => { + const targetFlags: LDFlagSet | undefined = { 'test-flag': false }; const flagChanges: LDFlagChangeset = { - 'another-test-flag': {current: false, previous: true} + 'another-test-flag': { current: false, previous: true }, }; const reactOptions: LDReactOptions = { useCamelCaseFlagKeys: true, }; const flattened = getFlattenedFlagsFromChangeset(flagChanges, targetFlags, reactOptions); - expect(flattened).toBeNull; - }) + expect(Object.keys(flattened)).toHaveLength(0); + }); test('getFlattenedFlagsFromChangeset should not change flags to camelCase when reactOptions.useCamelCaseFlagKeys is false ', () => { const targetFlags: LDFlagSet | undefined = undefined; const flagChanges: LDFlagChangeset = { - 'test-flag': {current: true, previous: false}, - 'another-test-flag': {current: false, previous: true} + 'test-flag': { current: true, previous: false }, + 'another-test-flag': { current: false, previous: true }, }; const reactOptions: LDReactOptions = { useCamelCaseFlagKeys: false, }; const flattened = getFlattenedFlagsFromChangeset(flagChanges, targetFlags, reactOptions); - expect(flattened).toEqual({'another-test-flag': false, 'test-flag': true}); - }) + expect(flattened).toEqual({ 'another-test-flag': false, 'test-flag': true }); + }); }); diff --git a/src/utils.ts b/src/utils.ts index 92103f1..66da194 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,6 @@ -import {LDFlagChangeset, LDFlagSet} from 'launchdarkly-js-client-sdk'; +import { LDFlagChangeset, LDFlagSet } from 'launchdarkly-js-client-sdk'; import camelCase from 'lodash.camelcase'; -import {LDReactOptions} from "./types"; +import { LDReactOptions } from './types'; /** * Transforms a set of flags so that their keys are camelCased. This function ignores @@ -27,12 +27,15 @@ export const camelCaseKeys = (rawFlags: LDFlagSet) => { * @param changes the `LDFlagChangeset` from the ldClient onchange handler. * @param targetFlags if targetFlags are specified, changes to other flags are ignored and not returned in the * flattened `LDFlagSet` - * @param reactOptions reactOptions.useCamelCaseFlagKeys is used to determine whether to - * @return an `LDFlagSet` with the current flag values from the LDFlagChangeset filtered by `targetFlags` - * or null if none of the targetFlags were changed + * @param reactOptions reactOptions.useCamelCaseFlagKeys determines whether to change the flag keys to camelCase + * @return an `LDFlagSet` with the current flag values from the LDFlagChangeset filtered by `targetFlags`. The returned + * object may be empty `{}` if none of the targetFlags were changed. */ -export const getFlattenedFlagsFromChangeset = (changes: LDFlagChangeset, targetFlags: LDFlagSet | undefined, - reactOptions: LDReactOptions): LDFlagSet | null => { +export const getFlattenedFlagsFromChangeset = ( + changes: LDFlagChangeset, + targetFlags: LDFlagSet | undefined, + reactOptions: LDReactOptions, +): LDFlagSet => { const flattened: LDFlagSet = {}; for (const key in changes) { if (targetFlags === undefined || targetFlags[key] !== undefined) { @@ -41,7 +44,8 @@ export const getFlattenedFlagsFromChangeset = (changes: LDFlagChangeset, targetF flattened[flagKey] = changes[key].current; } } - return Object.keys(flattened).length > 0 ? flattened : null; -} + + return flattened; +}; export default { camelCaseKeys, getFlattenedFlagsFromChangeset };